@ainyc/canonry 4.72.3 → 4.74.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 (27) hide show
  1. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +17 -0
  2. package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +56 -3
  3. package/assets/assets/{BacklinksPage-CjfpwZEH.js → BacklinksPage-ClgP7CUd.js} +1 -1
  4. package/assets/assets/{ChartPrimitives-Ckf2FrUy.js → ChartPrimitives-PGDrQBXP.js} +1 -1
  5. package/assets/assets/ProjectPage-xfLeh2vB.js +6 -0
  6. package/assets/assets/{RunRow-BuFyG0V_.js → RunRow-DL-lUm35.js} +1 -1
  7. package/assets/assets/{RunsPage-D-pr000K.js → RunsPage-BCL_lU-R.js} +1 -1
  8. package/assets/assets/{SettingsPage-CiaapCYn.js → SettingsPage-D67UQYJa.js} +1 -1
  9. package/assets/assets/{TrafficPage-B40xytJD.js → TrafficPage-DVRcPxCk.js} +1 -1
  10. package/assets/assets/{TrafficSourceDetailPage-7hHem-gM.js → TrafficSourceDetailPage-JzX1fhGQ.js} +1 -1
  11. package/assets/assets/{extract-error-message-3GkDsu1h.js → extract-error-message-Cia_CilL.js} +1 -1
  12. package/assets/assets/index-CFVX11lK.css +1 -0
  13. package/assets/assets/{index-BVdH2O9w.js → index-DHg9_-PB.js} +118 -118
  14. package/assets/assets/{server-traffic-CsgPsudZ.js → server-traffic-GBmLS3L7.js} +1 -1
  15. package/assets/assets/{trash-2-B8Ipf9rI.js → trash-2-Bk7PYGBN.js} +1 -1
  16. package/assets/index.html +2 -2
  17. package/dist/{chunk-JXFNERK4.js → chunk-A7JX3FZB.js} +1094 -995
  18. package/dist/{chunk-SIB4NMEH.js → chunk-MRC4JMIH.js} +369 -176
  19. package/dist/{chunk-ZUBBADMR.js → chunk-W6GBIRFA.js} +162 -1
  20. package/dist/{chunk-HSX32G47.js → chunk-ZRZHIS22.js} +414 -73
  21. package/dist/cli.js +236 -30
  22. package/dist/index.js +4 -4
  23. package/dist/{intelligence-service-ZW3ARLJT.js → intelligence-service-GPO2VMEC.js} +2 -2
  24. package/dist/mcp.js +2 -2
  25. package/package.json +8 -8
  26. package/assets/assets/ProjectPage-DZeplYeC.js +0 -6
  27. package/assets/assets/index-B3nENtU0.css +0 -1
@@ -9,7 +9,7 @@ import {
9
9
  loadConfig,
10
10
  loadConfigRaw,
11
11
  saveConfigPatch
12
- } from "./chunk-ZUBBADMR.js";
12
+ } from "./chunk-W6GBIRFA.js";
13
13
  import {
14
14
  CC_CACHE_DIR,
15
15
  DUCKDB_SPEC,
@@ -94,8 +94,10 @@ import {
94
94
  resolveWebhookTarget,
95
95
  runs,
96
96
  schedules,
97
+ siteAuditPages,
98
+ siteAuditSnapshots,
97
99
  usageCounters
98
- } from "./chunk-HSX32G47.js";
100
+ } from "./chunk-ZRZHIS22.js";
99
101
  import {
100
102
  AGENT_MEMORY_VALUE_MAX_BYTES,
101
103
  AGENT_PROVIDER_IDS,
@@ -133,6 +135,7 @@ import {
133
135
  determineAnswerMentioned,
134
136
  effectiveBrandNames,
135
137
  effectiveDomains,
138
+ factorStatusFromScore,
136
139
  isAgentProviderId,
137
140
  isBrowserProvider,
138
141
  isRetryableHttpError,
@@ -145,7 +148,7 @@ import {
145
148
  validationError,
146
149
  winnabilityClassLabel,
147
150
  withRetry
148
- } from "./chunk-JXFNERK4.js";
151
+ } from "./chunk-A7JX3FZB.js";
149
152
 
150
153
  // src/telemetry.ts
151
154
  import crypto from "crypto";
@@ -433,11 +436,11 @@ function checkLatestVersionForServer(opts) {
433
436
 
434
437
  // src/server.ts
435
438
  import { createRequire as createRequire3 } from "module";
436
- import crypto17 from "crypto";
439
+ import crypto18 from "crypto";
437
440
  import fs8 from "fs";
438
441
  import path9 from "path";
439
442
  import { fileURLToPath as fileURLToPath3 } from "url";
440
- import { and as and13, eq as eq17 } from "drizzle-orm";
443
+ import { and as and13, eq as eq18 } from "drizzle-orm";
441
444
  import Fastify from "fastify";
442
445
  import os5 from "os";
443
446
 
@@ -5162,8 +5165,158 @@ function buildDiscoveryInsightTitle(input) {
5162
5165
  return parts.join(" \u2022 ");
5163
5166
  }
5164
5167
 
5168
+ // src/execute-site-audit.ts
5169
+ import crypto12 from "crypto";
5170
+ import { eq as eq10 } from "drizzle-orm";
5171
+ import { runSitemapAudit } from "@ainyc/aeo-audit";
5172
+ var log11 = createLogger("SiteAudit");
5173
+ var SITE_AUDIT_DEFAULT_PAGE_LIMIT = 500;
5174
+ var SITE_AUDIT_MAX_PAGE_LIMIT = 2e3;
5175
+ function toHomepageUrl(canonicalDomain) {
5176
+ const trimmed = canonicalDomain.trim().replace(/\/+$/, "");
5177
+ return /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
5178
+ }
5179
+ function clampSiteAuditLimit(limit) {
5180
+ if (limit == null || !Number.isFinite(limit)) return SITE_AUDIT_DEFAULT_PAGE_LIMIT;
5181
+ return Math.max(1, Math.min(SITE_AUDIT_MAX_PAGE_LIMIT, Math.floor(limit)));
5182
+ }
5183
+ function toPageFactor(factor) {
5184
+ return {
5185
+ id: factor.id,
5186
+ name: factor.name,
5187
+ weight: factor.weight,
5188
+ score: factor.score
5189
+ };
5190
+ }
5191
+ function computeFactorAverages(pages) {
5192
+ const byId = /* @__PURE__ */ new Map();
5193
+ for (const page of pages) {
5194
+ if (page.status !== "success" || !page.factors) continue;
5195
+ for (const factor of page.factors) {
5196
+ let entry = byId.get(factor.id);
5197
+ if (!entry) {
5198
+ entry = { name: factor.name, weight: factor.weight, scores: [], pass: 0, partial: 0, fail: 0 };
5199
+ byId.set(factor.id, entry);
5200
+ }
5201
+ entry.scores.push(factor.score);
5202
+ const status = factorStatusFromScore(factor.score);
5203
+ if (status === "pass") entry.pass++;
5204
+ else if (status === "partial") entry.partial++;
5205
+ else entry.fail++;
5206
+ }
5207
+ }
5208
+ const summaries = [];
5209
+ for (const [id, entry] of byId) {
5210
+ const avgScore = entry.scores.length ? Math.round(entry.scores.reduce((sum, score) => sum + score, 0) / entry.scores.length) : 0;
5211
+ summaries.push({
5212
+ id,
5213
+ name: entry.name,
5214
+ weight: entry.weight,
5215
+ avgScore,
5216
+ status: factorStatusFromScore(avgScore),
5217
+ pagesPassing: entry.pass,
5218
+ pagesPartial: entry.partial,
5219
+ pagesFailing: entry.fail
5220
+ });
5221
+ }
5222
+ summaries.sort((a, b) => b.weight - a.weight || a.name.localeCompare(b.name));
5223
+ return summaries;
5224
+ }
5225
+ async function executeSiteAudit(db, runId, projectId, opts = {}) {
5226
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
5227
+ db.update(runs).set({ status: "running", startedAt }).where(eq10(runs.id, runId)).run();
5228
+ try {
5229
+ const project = db.select().from(projects).where(eq10(projects.id, projectId)).get();
5230
+ if (!project) {
5231
+ throw new Error(`Project not found: ${projectId}`);
5232
+ }
5233
+ const homepageUrl = toHomepageUrl(project.canonicalDomain);
5234
+ const limit = clampSiteAuditLimit(opts.limit);
5235
+ log11.info("start", { runId, projectId, homepageUrl, sitemapUrl: opts.sitemapUrl ?? null, limit });
5236
+ const report = await runSitemapAudit(homepageUrl, { sitemapUrl: opts.sitemapUrl, limit });
5237
+ const successCount = report.pages.filter((page) => page.status === "success").length;
5238
+ const pagesErrored = report.pages.filter((page) => page.status === "error").length;
5239
+ const auditable = report.pagesDiscovered - report.pagesSkipped;
5240
+ if (auditable > report.pagesAudited) {
5241
+ log11.info("truncated", {
5242
+ runId,
5243
+ projectId,
5244
+ auditable,
5245
+ audited: report.pagesAudited,
5246
+ dropped: auditable - report.pagesAudited,
5247
+ limit
5248
+ });
5249
+ }
5250
+ if (successCount === 0) {
5251
+ throw new Error(
5252
+ `Site audit could not successfully audit any of ${report.pagesAudited} page(s) from ${report.sitemapUrl}.`
5253
+ );
5254
+ }
5255
+ const factorAverages = computeFactorAverages(report.pages);
5256
+ const status = pagesErrored === 0 ? "completed" : "partial";
5257
+ const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
5258
+ db.transaction((tx) => {
5259
+ tx.insert(siteAuditSnapshots).values({
5260
+ id: crypto12.randomUUID(),
5261
+ projectId,
5262
+ runId,
5263
+ sitemapUrl: report.sitemapUrl,
5264
+ auditedAt: report.auditedAt,
5265
+ aggregateScore: report.aggregateScore,
5266
+ pagesDiscovered: report.pagesDiscovered,
5267
+ pagesAudited: report.pagesAudited,
5268
+ pagesSkipped: report.pagesSkipped,
5269
+ pagesErrored,
5270
+ factorAverages,
5271
+ // aeo-audit v3 enriches these (topIssues, avgGrade-free); keep only the
5272
+ // fields our DTO exposes so the stored JSON stays lean.
5273
+ crossCuttingIssues: report.crossCuttingIssues.map((issue) => ({
5274
+ factorId: issue.factorId,
5275
+ factorName: issue.factorName,
5276
+ avgScore: issue.avgScore,
5277
+ affectedPages: issue.affectedPages,
5278
+ totalPages: issue.totalPages,
5279
+ affectedPct: issue.totalPages > 0 ? Math.round(issue.affectedPages / issue.totalPages * 100) : 0,
5280
+ topRecommendations: issue.topRecommendations
5281
+ })),
5282
+ // v3 prioritizedFixes are structured PrioritizedFix objects; persist the
5283
+ // ready-to-display one-line summary to keep the DTO a string list.
5284
+ prioritizedFixes: report.prioritizedFixes.map((fix) => fix.summary),
5285
+ createdAt: finishedAt
5286
+ }).run();
5287
+ for (const page of report.pages) {
5288
+ tx.insert(siteAuditPages).values({
5289
+ id: crypto12.randomUUID(),
5290
+ projectId,
5291
+ runId,
5292
+ url: page.url,
5293
+ overallScore: page.overallScore,
5294
+ status: page.status,
5295
+ error: page.error ?? null,
5296
+ factors: (page.factors ?? []).map(toPageFactor),
5297
+ createdAt: finishedAt
5298
+ }).run();
5299
+ }
5300
+ tx.update(runs).set({ status, finishedAt }).where(eq10(runs.id, runId)).run();
5301
+ });
5302
+ log11.info("completed", {
5303
+ runId,
5304
+ projectId,
5305
+ status,
5306
+ score: report.aggregateScore,
5307
+ audited: report.pagesAudited,
5308
+ errored: pagesErrored
5309
+ });
5310
+ } catch (err) {
5311
+ const errorMsg = err instanceof Error ? err.message : String(err);
5312
+ db.update(runs).set({ status: "failed", error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq10(runs.id, runId)).run();
5313
+ log11.error("failed", { runId, projectId, error: errorMsg });
5314
+ throw err;
5315
+ }
5316
+ }
5317
+
5165
5318
  // src/commands/backfill.ts
5166
- import { and as and9, eq as eq10, inArray as inArray4, isNull, sql as sql4 } from "drizzle-orm";
5319
+ import { and as and9, eq as eq11, inArray as inArray4, isNull, sql as sql4 } from "drizzle-orm";
5167
5320
  var SNAPSHOT_BATCH_SIZE = 500;
5168
5321
  async function backfillAnswerVisibilityCommand(opts) {
5169
5322
  const config = loadConfig();
@@ -5171,7 +5324,7 @@ async function backfillAnswerVisibilityCommand(opts) {
5171
5324
  migrate(db);
5172
5325
  const projectFilter = opts?.project?.trim();
5173
5326
  const isDryRun = opts?.dryRun === true;
5174
- const scopedProjects = projectFilter ? db.select().from(projects).where(eq10(projects.name, projectFilter)).all() : db.select().from(projects).all();
5327
+ const scopedProjects = projectFilter ? db.select().from(projects).where(eq11(projects.name, projectFilter)).all() : db.select().from(projects).all();
5175
5328
  let examined = 0;
5176
5329
  let updated = 0;
5177
5330
  let wouldUpdate = 0;
@@ -5180,9 +5333,9 @@ async function backfillAnswerVisibilityCommand(opts) {
5180
5333
  let providerErrors = 0;
5181
5334
  if (scopedProjects.length > 0) {
5182
5335
  const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and9(
5183
- eq10(runs.kind, RunKinds["answer-visibility"]),
5336
+ eq11(runs.kind, RunKinds["answer-visibility"]),
5184
5337
  inArray4(runs.projectId, scopedProjects.map((project) => project.id))
5185
- )).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(eq10(runs.kind, RunKinds["answer-visibility"])).all();
5338
+ )).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(eq11(runs.kind, RunKinds["answer-visibility"])).all();
5186
5339
  const runIdsByProject = /* @__PURE__ */ new Map();
5187
5340
  for (const run of runRows) {
5188
5341
  const existing = runIdsByProject.get(run.projectId);
@@ -5190,7 +5343,7 @@ async function backfillAnswerVisibilityCommand(opts) {
5190
5343
  else runIdsByProject.set(run.projectId, [run.id]);
5191
5344
  }
5192
5345
  for (const project of scopedProjects) {
5193
- const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq10(competitors.projectId, project.id)).all().map((row) => row.domain);
5346
+ const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq11(competitors.projectId, project.id)).all().map((row) => row.domain);
5194
5347
  const runIds = runIdsByProject.get(project.id) ?? [];
5195
5348
  if (runIds.length === 0) continue;
5196
5349
  const projectDomains = effectiveDomains({
@@ -5278,7 +5431,7 @@ async function backfillAnswerVisibilityCommand(opts) {
5278
5431
  } else {
5279
5432
  db.transaction((tx) => {
5280
5433
  for (const update of pendingUpdates) {
5281
- tx.update(querySnapshots).set(update.patch).where(eq10(querySnapshots.id, update.id)).run();
5434
+ tx.update(querySnapshots).set(update.patch).where(eq11(querySnapshots.id, update.id)).run();
5282
5435
  }
5283
5436
  });
5284
5437
  updated += pendingUpdates.length;
@@ -5327,7 +5480,7 @@ No DB writes performed. Re-run without --dry-run to apply.`);
5327
5480
  function backfillNormalizedPaths(db, opts) {
5328
5481
  const baseConditions = [];
5329
5482
  if (opts?.projectId) {
5330
- baseConditions.push(eq10(gaTrafficSnapshots.projectId, opts.projectId));
5483
+ baseConditions.push(eq11(gaTrafficSnapshots.projectId, opts.projectId));
5331
5484
  }
5332
5485
  const rows = db.select({
5333
5486
  id: gaTrafficSnapshots.id,
@@ -5348,7 +5501,7 @@ function backfillNormalizedPaths(db, opts) {
5348
5501
  unchanged++;
5349
5502
  continue;
5350
5503
  }
5351
- tx.update(gaTrafficSnapshots).set({ landingPageNormalized: next }).where(eq10(gaTrafficSnapshots.id, row.id)).run();
5504
+ tx.update(gaTrafficSnapshots).set({ landingPageNormalized: next }).where(eq11(gaTrafficSnapshots.id, row.id)).run();
5352
5505
  updated++;
5353
5506
  }
5354
5507
  });
@@ -5362,7 +5515,7 @@ async function backfillNormalizedPathsCommand(opts) {
5362
5515
  const projectFilter = opts?.project?.trim();
5363
5516
  let projectId;
5364
5517
  if (projectFilter) {
5365
- const project = db.select({ id: projects.id }).from(projects).where(eq10(projects.name, projectFilter)).get();
5518
+ const project = db.select({ id: projects.id }).from(projects).where(eq11(projects.name, projectFilter)).get();
5366
5519
  if (!project) {
5367
5520
  const result2 = {
5368
5521
  project: projectFilter,
@@ -5399,7 +5552,7 @@ async function backfillNormalizedPathsCommand(opts) {
5399
5552
  function backfillAiReferralPaths(db, opts) {
5400
5553
  const baseConditions = [];
5401
5554
  if (opts?.projectId) {
5402
- baseConditions.push(eq10(gaAiReferrals.projectId, opts.projectId));
5555
+ baseConditions.push(eq11(gaAiReferrals.projectId, opts.projectId));
5403
5556
  }
5404
5557
  const rows = db.select({
5405
5558
  id: gaAiReferrals.id,
@@ -5420,7 +5573,7 @@ function backfillAiReferralPaths(db, opts) {
5420
5573
  unchanged++;
5421
5574
  continue;
5422
5575
  }
5423
- tx.update(gaAiReferrals).set({ landingPageNormalized: next }).where(eq10(gaAiReferrals.id, row.id)).run();
5576
+ tx.update(gaAiReferrals).set({ landingPageNormalized: next }).where(eq11(gaAiReferrals.id, row.id)).run();
5424
5577
  updated++;
5425
5578
  }
5426
5579
  });
@@ -5434,7 +5587,7 @@ async function backfillAiReferralPathsCommand(opts) {
5434
5587
  const projectFilter = opts?.project?.trim();
5435
5588
  let projectId;
5436
5589
  if (projectFilter) {
5437
- const project = db.select({ id: projects.id }).from(projects).where(eq10(projects.name, projectFilter)).get();
5590
+ const project = db.select({ id: projects.id }).from(projects).where(eq11(projects.name, projectFilter)).get();
5438
5591
  if (!project) {
5439
5592
  const result2 = {
5440
5593
  project: projectFilter,
@@ -5470,10 +5623,10 @@ async function backfillAiReferralPathsCommand(opts) {
5470
5623
  }
5471
5624
  function backfillProjectAnswerMentions(db, projectId, opts) {
5472
5625
  const isDryRun = opts?.dryRun === true;
5473
- const project = db.select().from(projects).where(eq10(projects.id, projectId)).get();
5626
+ const project = db.select().from(projects).where(eq11(projects.id, projectId)).get();
5474
5627
  if (!project) return { examined: 0, updated: 0, mentioned: 0 };
5475
- const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq10(competitors.projectId, projectId)).all().map((row) => row.domain);
5476
- const runRows = db.select({ id: runs.id }).from(runs).where(and9(eq10(runs.kind, RunKinds["answer-visibility"]), eq10(runs.projectId, projectId))).all();
5628
+ const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq11(competitors.projectId, projectId)).all().map((row) => row.domain);
5629
+ const runRows = db.select({ id: runs.id }).from(runs).where(and9(eq11(runs.kind, RunKinds["answer-visibility"]), eq11(runs.projectId, projectId))).all();
5477
5630
  const runIds = runRows.map((r) => r.id);
5478
5631
  let examined = 0;
5479
5632
  let updated = 0;
@@ -5545,7 +5698,7 @@ function backfillProjectAnswerMentions(db, projectId, opts) {
5545
5698
  } else {
5546
5699
  db.transaction((tx) => {
5547
5700
  for (const update of pendingUpdates) {
5548
- tx.update(querySnapshots).set(update.patch).where(eq10(querySnapshots.id, update.id)).run();
5701
+ tx.update(querySnapshots).set(update.patch).where(eq11(querySnapshots.id, update.id)).run();
5549
5702
  }
5550
5703
  });
5551
5704
  updated += pendingUpdates.length;
@@ -5560,7 +5713,7 @@ async function backfillAnswerMentionsCommand(opts) {
5560
5713
  migrate(db);
5561
5714
  const projectFilter = opts?.project?.trim();
5562
5715
  const isDryRun = opts?.dryRun === true;
5563
- const scopedProjects = projectFilter ? db.select().from(projects).where(eq10(projects.name, projectFilter)).all() : db.select().from(projects).all();
5716
+ const scopedProjects = projectFilter ? db.select().from(projects).where(eq11(projects.name, projectFilter)).all() : db.select().from(projects).all();
5564
5717
  let examined = 0;
5565
5718
  let updated = 0;
5566
5719
  let wouldUpdate = 0;
@@ -5620,7 +5773,7 @@ function readStoredGroundingSources(rawResponse) {
5620
5773
  return result;
5621
5774
  }
5622
5775
  async function backfillInsightsCommand(project, opts) {
5623
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-ZW3ARLJT.js");
5776
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-GPO2VMEC.js");
5624
5777
  const config = loadConfig();
5625
5778
  const db = createClient(config.database);
5626
5779
  migrate(db);
@@ -5779,7 +5932,7 @@ async function backfillSnapshotAttributionCommand(opts) {
5779
5932
  const config = loadConfig();
5780
5933
  const db = createClient(config.database);
5781
5934
  migrate(db);
5782
- const project = db.select().from(projects).where(eq10(projects.name, opts.project)).get();
5935
+ const project = db.select().from(projects).where(eq11(projects.name, opts.project)).get();
5783
5936
  if (!project) {
5784
5937
  throw new Error(`Project "${opts.project}" not found`);
5785
5938
  }
@@ -5791,7 +5944,7 @@ async function backfillSnapshotAttributionCommand(opts) {
5791
5944
  `);
5792
5945
  }
5793
5946
  const events = db.select({ createdAt: auditLog.createdAt, action: auditLog.action, diff: auditLog.diff }).from(auditLog).where(and9(
5794
- eq10(auditLog.projectId, project.id),
5947
+ eq11(auditLog.projectId, project.id),
5795
5948
  inArray4(auditLog.action, ["keywords.appended", "keywords.deleted", "queries.appended", "queries.deleted", "queries.replaced"])
5796
5949
  )).orderBy(auditLog.createdAt).all();
5797
5950
  const history = replayQueryAuditLog(events);
@@ -5799,8 +5952,8 @@ async function backfillSnapshotAttributionCommand(opts) {
5799
5952
  runId: runs.id,
5800
5953
  createdAt: runs.createdAt,
5801
5954
  location: runs.location
5802
- }).from(runs).innerJoin(querySnapshots, eq10(querySnapshots.runId, runs.id)).where(and9(
5803
- eq10(runs.projectId, project.id),
5955
+ }).from(runs).innerJoin(querySnapshots, eq11(querySnapshots.runId, runs.id)).where(and9(
5956
+ eq11(runs.projectId, project.id),
5804
5957
  isNull(querySnapshots.queryId),
5805
5958
  isNull(querySnapshots.queryText)
5806
5959
  )).groupBy(runs.id).orderBy(runs.createdAt).all();
@@ -5823,7 +5976,7 @@ async function backfillSnapshotAttributionCommand(opts) {
5823
5976
  createdAt: querySnapshots.createdAt,
5824
5977
  answerText: querySnapshots.answerText
5825
5978
  }).from(querySnapshots).where(and9(
5826
- eq10(querySnapshots.runId, run.runId),
5979
+ eq11(querySnapshots.runId, run.runId),
5827
5980
  isNull(querySnapshots.queryId),
5828
5981
  isNull(querySnapshots.queryText)
5829
5982
  )).orderBy(querySnapshots.provider, querySnapshots.createdAt).all();
@@ -5889,7 +6042,7 @@ async function backfillSnapshotAttributionCommand(opts) {
5889
6042
  if (!isDryRun && updates.length > 0) {
5890
6043
  db.transaction((tx) => {
5891
6044
  for (const u of updates) {
5892
- tx.update(querySnapshots).set({ queryText: u.queryText }).where(eq10(querySnapshots.id, u.id)).run();
6045
+ tx.update(querySnapshots).set({ queryText: u.queryText }).where(eq11(querySnapshots.id, u.id)).run();
5893
6046
  }
5894
6047
  });
5895
6048
  }
@@ -5963,7 +6116,7 @@ async function backfillTrafficClassificationCommand(opts) {
5963
6116
  const projectFilter = opts?.project?.trim();
5964
6117
  const isDryRun = opts?.dryRun === true;
5965
6118
  const isJson = isMachineFormat(opts?.format);
5966
- const scopedProjects = projectFilter ? db.select().from(projects).where(eq10(projects.name, projectFilter)).all() : db.select().from(projects).all();
6119
+ const scopedProjects = projectFilter ? db.select().from(projects).where(eq11(projects.name, projectFilter)).all() : db.select().from(projects).all();
5967
6120
  if (scopedProjects.length === 0) {
5968
6121
  if (projectFilter && !isJson) {
5969
6122
  process.stderr.write(`No project named "${projectFilter}".
@@ -5989,7 +6142,7 @@ async function backfillTrafficClassificationCommand(opts) {
5989
6142
  byBot: {}
5990
6143
  };
5991
6144
  const unknownCountRow = db.select({ n: sql4`count(*)` }).from(rawEventSamples).where(and9(
5992
- eq10(rawEventSamples.eventType, "unknown"),
6145
+ eq11(rawEventSamples.eventType, "unknown"),
5993
6146
  inArray4(rawEventSamples.projectId, projectIds)
5994
6147
  )).get();
5995
6148
  result.unknownBefore = Number(unknownCountRow?.n ?? 0);
@@ -6002,7 +6155,7 @@ async function backfillTrafficClassificationCommand(opts) {
6002
6155
  pathNormalized: rawEventSamples.pathNormalized,
6003
6156
  status: rawEventSamples.status
6004
6157
  }).from(rawEventSamples).where(and9(
6005
- eq10(rawEventSamples.eventType, "unknown"),
6158
+ eq11(rawEventSamples.eventType, "unknown"),
6006
6159
  inArray4(rawEventSamples.projectId, projectIds)
6007
6160
  )).all();
6008
6161
  result.examined = unknownSamples.length;
@@ -6041,7 +6194,7 @@ async function backfillTrafficClassificationCommand(opts) {
6041
6194
  result.reclassified++;
6042
6195
  result.byBot[classified.botId] = (result.byBot[classified.botId] ?? 0) + 1;
6043
6196
  if (isDryRun) continue;
6044
- db.update(rawEventSamples).set({ eventType: userFetch ? TrafficEventKinds["ai-user-fetch"] : TrafficEventKinds.crawler }).where(eq10(rawEventSamples.id, snap.id)).run();
6197
+ db.update(rawEventSamples).set({ eventType: userFetch ? TrafficEventKinds["ai-user-fetch"] : TrafficEventKinds.crawler }).where(eq11(rawEventSamples.id, snap.id)).run();
6045
6198
  const tsHour = new Date(snap.ts);
6046
6199
  tsHour.setUTCMinutes(0, 0, 0);
6047
6200
  if (userFetch) {
@@ -6106,7 +6259,7 @@ async function backfillTrafficClassificationCommand(opts) {
6106
6259
  }
6107
6260
  if (!isDryRun) {
6108
6261
  const afterRow = db.select({ n: sql4`count(*)` }).from(rawEventSamples).where(and9(
6109
- eq10(rawEventSamples.eventType, "unknown"),
6262
+ eq11(rawEventSamples.eventType, "unknown"),
6110
6263
  inArray4(rawEventSamples.projectId, projectIds)
6111
6264
  )).get();
6112
6265
  result.unknownAfter = Number(afterRow?.n ?? 0);
@@ -6141,7 +6294,7 @@ No DB writes performed. Re-run without --dry-run to apply.`);
6141
6294
  }
6142
6295
 
6143
6296
  // src/commands/skills.ts
6144
- import crypto12 from "crypto";
6297
+ import crypto13 from "crypto";
6145
6298
  import fs4 from "fs";
6146
6299
  import os4 from "os";
6147
6300
  import path5 from "path";
@@ -6196,7 +6349,7 @@ function walkRelative(dir, prefix = "") {
6196
6349
  return out.sort();
6197
6350
  }
6198
6351
  function sha256File(filePath) {
6199
- return crypto12.createHash("sha256").update(fs4.readFileSync(filePath)).digest("hex");
6352
+ return crypto13.createHash("sha256").update(fs4.readFileSync(filePath)).digest("hex");
6200
6353
  }
6201
6354
  function readSkillManifest(skillDir) {
6202
6355
  try {
@@ -6519,10 +6672,10 @@ var ProviderRegistry = class {
6519
6672
  };
6520
6673
 
6521
6674
  // src/scheduler.ts
6522
- import crypto13 from "crypto";
6675
+ import crypto14 from "crypto";
6523
6676
  import cron from "node-cron";
6524
- import { and as and10, eq as eq11 } from "drizzle-orm";
6525
- var log11 = createLogger("Scheduler");
6677
+ import { and as and10, eq as eq12, inArray as inArray5 } from "drizzle-orm";
6678
+ var log12 = createLogger("Scheduler");
6526
6679
  function taskKey(projectId, kind) {
6527
6680
  return `${projectId}::${kind}`;
6528
6681
  }
@@ -6536,16 +6689,16 @@ var Scheduler = class {
6536
6689
  }
6537
6690
  /** Load all enabled schedules from DB and register cron jobs. */
6538
6691
  start() {
6539
- const allSchedules = this.db.select().from(schedules).where(eq11(schedules.enabled, true)).all();
6692
+ const allSchedules = this.db.select().from(schedules).where(eq12(schedules.enabled, true)).all();
6540
6693
  for (const schedule of allSchedules) {
6541
6694
  const missedRunAt = schedule.nextRunAt;
6542
6695
  this.registerCronTask(schedule);
6543
6696
  if (missedRunAt && new Date(missedRunAt) < /* @__PURE__ */ new Date()) {
6544
- log11.info("run.catch-up", { projectId: schedule.projectId, kind: schedule.kind, missedRunAt });
6697
+ log12.info("run.catch-up", { projectId: schedule.projectId, kind: schedule.kind, missedRunAt });
6545
6698
  this.triggerRun(schedule.id, schedule.projectId, schedule.kind);
6546
6699
  }
6547
6700
  }
6548
- log11.info("started", { scheduleCount: allSchedules.length });
6701
+ log12.info("started", { scheduleCount: allSchedules.length });
6549
6702
  }
6550
6703
  /** Stop all cron tasks for graceful shutdown. */
6551
6704
  stop() {
@@ -6566,7 +6719,7 @@ var Scheduler = class {
6566
6719
  this.stopTask(key, existing, "Stopped");
6567
6720
  this.tasks.delete(key);
6568
6721
  }
6569
- const schedule = this.db.select().from(schedules).where(and10(eq11(schedules.projectId, projectId), eq11(schedules.kind, kind))).get();
6722
+ const schedule = this.db.select().from(schedules).where(and10(eq12(schedules.projectId, projectId), eq12(schedules.kind, kind))).get();
6570
6723
  if (schedule && schedule.enabled) {
6571
6724
  this.registerCronTask(schedule);
6572
6725
  }
@@ -6589,13 +6742,13 @@ var Scheduler = class {
6589
6742
  stopTask(key, task, verb) {
6590
6743
  void task.stop();
6591
6744
  void task.destroy();
6592
- log11.info(`task.${verb.toLowerCase()}`, { key });
6745
+ log12.info(`task.${verb.toLowerCase()}`, { key });
6593
6746
  }
6594
6747
  registerCronTask(schedule) {
6595
6748
  const { id: scheduleId, projectId, cronExpr, timezone } = schedule;
6596
6749
  const kind = schedule.kind;
6597
6750
  if (!cron.validate(cronExpr)) {
6598
- log11.error("cron.invalid", { projectId, kind, cronExpr });
6751
+ log12.error("cron.invalid", { projectId, kind, cronExpr });
6599
6752
  return;
6600
6753
  }
6601
6754
  const task = cron.schedule(cronExpr, () => {
@@ -6607,51 +6760,51 @@ var Scheduler = class {
6607
6760
  this.db.update(schedules).set({
6608
6761
  nextRunAt: nextRunFromCron(cronExpr, timezone),
6609
6762
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
6610
- }).where(eq11(schedules.id, scheduleId)).run();
6763
+ }).where(eq12(schedules.id, scheduleId)).run();
6611
6764
  const label = schedule.preset ?? cronExpr;
6612
- log11.info("cron.registered", { projectId, kind, schedule: label, timezone });
6765
+ log12.info("cron.registered", { projectId, kind, schedule: label, timezone });
6613
6766
  }
6614
6767
  triggerRun(scheduleId, projectId, kind) {
6615
6768
  try {
6616
6769
  const now = (/* @__PURE__ */ new Date()).toISOString();
6617
- const currentSchedule = this.db.select().from(schedules).where(eq11(schedules.id, scheduleId)).get();
6770
+ const currentSchedule = this.db.select().from(schedules).where(eq12(schedules.id, scheduleId)).get();
6618
6771
  if (!currentSchedule || !currentSchedule.enabled) {
6619
- log11.warn("schedule.stale", { scheduleId, projectId, kind, msg: "schedule no longer exists or is disabled" });
6772
+ log12.warn("schedule.stale", { scheduleId, projectId, kind, msg: "schedule no longer exists or is disabled" });
6620
6773
  this.remove(projectId, kind);
6621
6774
  return;
6622
6775
  }
6623
6776
  const nextRunAt = nextRunFromCron(currentSchedule.cronExpr, currentSchedule.timezone);
6624
- const project = this.db.select().from(projects).where(eq11(projects.id, projectId)).get();
6777
+ const project = this.db.select().from(projects).where(eq12(projects.id, projectId)).get();
6625
6778
  if (!project) {
6626
- log11.error("project.not-found", { projectId, kind, msg: "skipping scheduled run" });
6779
+ log12.error("project.not-found", { projectId, kind, msg: "skipping scheduled run" });
6627
6780
  this.remove(projectId, kind);
6628
6781
  return;
6629
6782
  }
6630
6783
  if (kind === SchedulableRunKinds["traffic-sync"]) {
6631
6784
  const sourceId = currentSchedule.sourceId;
6632
6785
  if (!sourceId) {
6633
- log11.warn("traffic-sync.missing-source", { scheduleId, projectId });
6786
+ log12.warn("traffic-sync.missing-source", { scheduleId, projectId });
6634
6787
  return;
6635
6788
  }
6636
6789
  if (!this.callbacks.onTrafficSyncRequested) {
6637
- log11.warn("traffic-sync.no-callback", { scheduleId, projectId, msg: "host did not register onTrafficSyncRequested" });
6790
+ log12.warn("traffic-sync.no-callback", { scheduleId, projectId, msg: "host did not register onTrafficSyncRequested" });
6638
6791
  return;
6639
6792
  }
6640
6793
  this.db.update(schedules).set({
6641
6794
  lastRunAt: now,
6642
6795
  nextRunAt,
6643
6796
  updatedAt: now
6644
- }).where(eq11(schedules.id, currentSchedule.id)).run();
6645
- log11.info("traffic-sync.triggered", { projectName: project.name, sourceId });
6797
+ }).where(eq12(schedules.id, currentSchedule.id)).run();
6798
+ log12.info("traffic-sync.triggered", { projectName: project.name, sourceId });
6646
6799
  this.callbacks.onTrafficSyncRequested(project.name, sourceId);
6647
6800
  return;
6648
6801
  }
6649
6802
  if (kind === SchedulableRunKinds["gbp-sync"]) {
6650
6803
  if (!this.callbacks.onGbpSyncRequested) {
6651
- log11.warn("gbp-sync.no-callback", { scheduleId, projectId, msg: "host did not register onGbpSyncRequested" });
6804
+ log12.warn("gbp-sync.no-callback", { scheduleId, projectId, msg: "host did not register onGbpSyncRequested" });
6652
6805
  return;
6653
6806
  }
6654
- const runId2 = crypto13.randomUUID();
6807
+ const runId2 = crypto14.randomUUID();
6655
6808
  this.db.insert(runs).values({
6656
6809
  id: runId2,
6657
6810
  projectId,
@@ -6664,45 +6817,78 @@ var Scheduler = class {
6664
6817
  lastRunAt: now,
6665
6818
  nextRunAt,
6666
6819
  updatedAt: now
6667
- }).where(eq11(schedules.id, currentSchedule.id)).run();
6668
- log11.info("gbp-sync.triggered", { runId: runId2, projectName: project.name });
6820
+ }).where(eq12(schedules.id, currentSchedule.id)).run();
6821
+ log12.info("gbp-sync.triggered", { runId: runId2, projectName: project.name });
6669
6822
  this.callbacks.onGbpSyncRequested(runId2, projectId);
6670
6823
  return;
6671
6824
  }
6672
6825
  if (kind === SchedulableRunKinds["data-refresh"]) {
6673
6826
  if (!this.callbacks.onDataRefreshRequested) {
6674
- log11.warn("data-refresh.no-callback", { scheduleId, projectId, msg: "host did not register onDataRefreshRequested" });
6827
+ log12.warn("data-refresh.no-callback", { scheduleId, projectId, msg: "host did not register onDataRefreshRequested" });
6675
6828
  return;
6676
6829
  }
6677
6830
  this.db.update(schedules).set({
6678
6831
  lastRunAt: now,
6679
6832
  nextRunAt,
6680
6833
  updatedAt: now
6681
- }).where(eq11(schedules.id, currentSchedule.id)).run();
6682
- log11.info("data-refresh.triggered", { projectName: project.name });
6834
+ }).where(eq12(schedules.id, currentSchedule.id)).run();
6835
+ log12.info("data-refresh.triggered", { projectName: project.name });
6683
6836
  this.callbacks.onDataRefreshRequested(project.name);
6684
6837
  return;
6685
6838
  }
6686
6839
  if (kind === SchedulableRunKinds["backlinks-sync"]) {
6687
6840
  if (!this.callbacks.onBacklinksSyncRequested) {
6688
- log11.warn("backlinks-sync.no-callback", { scheduleId, projectId, msg: "host did not register onBacklinksSyncRequested" });
6841
+ log12.warn("backlinks-sync.no-callback", { scheduleId, projectId, msg: "host did not register onBacklinksSyncRequested" });
6689
6842
  return;
6690
6843
  }
6691
6844
  this.db.update(schedules).set({
6692
6845
  lastRunAt: now,
6693
6846
  nextRunAt,
6694
6847
  updatedAt: now
6695
- }).where(eq11(schedules.id, currentSchedule.id)).run();
6696
- log11.info("backlinks-sync.triggered", { projectName: project.name });
6848
+ }).where(eq12(schedules.id, currentSchedule.id)).run();
6849
+ log12.info("backlinks-sync.triggered", { projectName: project.name });
6697
6850
  this.callbacks.onBacklinksSyncRequested(project.name);
6698
6851
  return;
6699
6852
  }
6853
+ if (kind === SchedulableRunKinds["site-audit"]) {
6854
+ if (!this.callbacks.onSiteAuditRequested) {
6855
+ log12.warn("site-audit.no-callback", { scheduleId, projectId, msg: "host did not register onSiteAuditRequested" });
6856
+ return;
6857
+ }
6858
+ const active = this.db.select({ id: runs.id }).from(runs).where(and10(
6859
+ eq12(runs.projectId, projectId),
6860
+ eq12(runs.kind, RunKinds["site-audit"]),
6861
+ inArray5(runs.status, [RunStatuses.queued, RunStatuses.running])
6862
+ )).get();
6863
+ if (active) {
6864
+ log12.info("site-audit.skipped-active", { projectName: project.name, activeRunId: active.id });
6865
+ this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(eq12(schedules.id, currentSchedule.id)).run();
6866
+ return;
6867
+ }
6868
+ const runId2 = crypto14.randomUUID();
6869
+ this.db.insert(runs).values({
6870
+ id: runId2,
6871
+ projectId,
6872
+ kind: RunKinds["site-audit"],
6873
+ status: RunStatuses.queued,
6874
+ trigger: RunTriggers.scheduled,
6875
+ createdAt: now
6876
+ }).run();
6877
+ this.db.update(schedules).set({
6878
+ lastRunAt: now,
6879
+ nextRunAt,
6880
+ updatedAt: now
6881
+ }).where(eq12(schedules.id, currentSchedule.id)).run();
6882
+ log12.info("site-audit.triggered", { runId: runId2, projectName: project.name });
6883
+ this.callbacks.onSiteAuditRequested(runId2, projectId);
6884
+ return;
6885
+ }
6700
6886
  const projectLocations = project.locations;
6701
6887
  let resolvedLocation;
6702
6888
  if (project.defaultLocation) {
6703
6889
  const loc = projectLocations.find((l) => l.label === project.defaultLocation);
6704
6890
  if (!loc) {
6705
- log11.warn("default-location.stale", { scheduleId, projectId, label: project.defaultLocation });
6891
+ log12.warn("default-location.stale", { scheduleId, projectId, label: project.defaultLocation });
6706
6892
  return;
6707
6893
  }
6708
6894
  resolvedLocation = loc;
@@ -6716,11 +6902,11 @@ var Scheduler = class {
6716
6902
  location: locationLabel
6717
6903
  });
6718
6904
  if (queueResult.conflict) {
6719
- log11.info("run.skipped-active", { projectName: project.name, activeRunId: queueResult.activeRunId });
6905
+ log12.info("run.skipped-active", { projectName: project.name, activeRunId: queueResult.activeRunId });
6720
6906
  this.db.update(schedules).set({
6721
6907
  nextRunAt,
6722
6908
  updatedAt: now
6723
- }).where(eq11(schedules.id, currentSchedule.id)).run();
6909
+ }).where(eq12(schedules.id, currentSchedule.id)).run();
6724
6910
  return;
6725
6911
  }
6726
6912
  const runId = queueResult.runId;
@@ -6728,19 +6914,19 @@ var Scheduler = class {
6728
6914
  lastRunAt: now,
6729
6915
  nextRunAt,
6730
6916
  updatedAt: now
6731
- }).where(eq11(schedules.id, currentSchedule.id)).run();
6917
+ }).where(eq12(schedules.id, currentSchedule.id)).run();
6732
6918
  const scheduleProviders = currentSchedule.providers;
6733
6919
  const providers = scheduleProviders.length > 0 ? scheduleProviders : void 0;
6734
- log11.info("run.triggered", { runId, projectName: project.name, providers: providers ?? "all" });
6920
+ log12.info("run.triggered", { runId, projectName: project.name, providers: providers ?? "all" });
6735
6921
  this.callbacks.onRunCreated(runId, projectId, providers, resolvedLocation);
6736
6922
  } catch (err) {
6737
- log11.error("trigger.error", { scheduleId, projectId, kind, error: err instanceof Error ? err.message : String(err) });
6923
+ log12.error("trigger.error", { scheduleId, projectId, kind, error: err instanceof Error ? err.message : String(err) });
6738
6924
  }
6739
6925
  }
6740
6926
  };
6741
6927
 
6742
6928
  // src/data-refresh.ts
6743
- var log12 = createLogger("DataRefresh");
6929
+ var log13 = createLogger("DataRefresh");
6744
6930
  async function refreshAllIntegrations(client, projectName) {
6745
6931
  const integrations = [
6746
6932
  { name: "gsc", run: () => client.gscSync(projectName, {}) },
@@ -6752,19 +6938,19 @@ async function refreshAllIntegrations(client, projectName) {
6752
6938
  results.forEach((result, idx) => {
6753
6939
  const integration = integrations[idx].name;
6754
6940
  if (result.status === "fulfilled") {
6755
- log12.info("integration.refreshed", { projectName, integration });
6941
+ log13.info("integration.refreshed", { projectName, integration });
6756
6942
  } else {
6757
6943
  const reason = result.reason;
6758
6944
  const message = reason instanceof Error ? reason.message : String(reason);
6759
- log12.warn("integration.refresh-failed", { projectName, integration, error: message });
6945
+ log13.warn("integration.refresh-failed", { projectName, integration, error: message });
6760
6946
  }
6761
6947
  });
6762
6948
  }
6763
6949
 
6764
6950
  // src/notifier.ts
6765
- import { eq as eq12, desc as desc5, and as and11, inArray as inArray5, or } from "drizzle-orm";
6766
- import crypto14 from "crypto";
6767
- var log13 = createLogger("Notifier");
6951
+ import { eq as eq13, desc as desc5, and as and11, inArray as inArray6, or } from "drizzle-orm";
6952
+ import crypto15 from "crypto";
6953
+ var log14 = createLogger("Notifier");
6768
6954
  var Notifier = class {
6769
6955
  db;
6770
6956
  serverUrl;
@@ -6774,26 +6960,26 @@ var Notifier = class {
6774
6960
  }
6775
6961
  /** Called after a run completes (success, partial, or failed). */
6776
6962
  async onRunCompleted(runId, projectId) {
6777
- log13.info("run.completed", { runId, projectId });
6778
- const notifs = this.db.select().from(notifications).where(eq12(notifications.projectId, projectId)).all().filter((n) => n.enabled);
6963
+ log14.info("run.completed", { runId, projectId });
6964
+ const notifs = this.db.select().from(notifications).where(eq13(notifications.projectId, projectId)).all().filter((n) => n.enabled);
6779
6965
  if (notifs.length === 0) {
6780
- log13.info("notifications.none-enabled", { projectId });
6966
+ log14.info("notifications.none-enabled", { projectId });
6781
6967
  return;
6782
6968
  }
6783
- log13.info("notifications.found", { projectId, count: notifs.length });
6784
- const run = this.db.select().from(runs).where(eq12(runs.id, runId)).get();
6969
+ log14.info("notifications.found", { projectId, count: notifs.length });
6970
+ const run = this.db.select().from(runs).where(eq13(runs.id, runId)).get();
6785
6971
  if (!run) {
6786
- log13.error("run.not-found", { runId, msg: "skipping notification dispatch" });
6972
+ log14.error("run.not-found", { runId, msg: "skipping notification dispatch" });
6787
6973
  return;
6788
6974
  }
6789
- const project = this.db.select().from(projects).where(eq12(projects.id, projectId)).get();
6975
+ const project = this.db.select().from(projects).where(eq13(projects.id, projectId)).get();
6790
6976
  if (!project) {
6791
- log13.error("project.not-found", { projectId, msg: "skipping notification dispatch" });
6977
+ log14.error("project.not-found", { projectId, msg: "skipping notification dispatch" });
6792
6978
  return;
6793
6979
  }
6794
6980
  const transitions = this.computeTransitions(runId, projectId);
6795
6981
  const events = [];
6796
- log13.info("run.status", { runId: run.id, status: run.status, projectId });
6982
+ log14.info("run.status", { runId: run.id, status: run.status, projectId });
6797
6983
  if (run.status === "completed" || run.status === "partial") {
6798
6984
  events.push("run.completed");
6799
6985
  }
@@ -6809,7 +6995,7 @@ var Notifier = class {
6809
6995
  if (!config.url) continue;
6810
6996
  const subscribedEvents = config.events;
6811
6997
  const matchingEvents = events.filter((e) => subscribedEvents.includes(e));
6812
- log13.info("notification.match", { notificationId: notif.id, subscribedEvents, matchedEvents: matchingEvents });
6998
+ log14.info("notification.match", { notificationId: notif.id, subscribedEvents, matchedEvents: matchingEvents });
6813
6999
  if (matchingEvents.length === 0) continue;
6814
7000
  for (const event of matchingEvents) {
6815
7001
  const relevantTransitions = event === "citation.lost" ? lostTransitions : event === "citation.gained" ? gainedTransitions : transitions;
@@ -6833,11 +7019,11 @@ var Notifier = class {
6833
7019
  if (criticalInsights.length > 0) insightEvents.push("insight.critical");
6834
7020
  if (highInsights.length > 0) insightEvents.push("insight.high");
6835
7021
  if (insightEvents.length === 0) return;
6836
- const notifs = this.db.select().from(notifications).where(eq12(notifications.projectId, projectId)).all().filter((n) => n.enabled);
7022
+ const notifs = this.db.select().from(notifications).where(eq13(notifications.projectId, projectId)).all().filter((n) => n.enabled);
6837
7023
  if (notifs.length === 0) return;
6838
- const run = this.db.select().from(runs).where(eq12(runs.id, runId)).get();
7024
+ const run = this.db.select().from(runs).where(eq13(runs.id, runId)).get();
6839
7025
  if (!run) return;
6840
- const project = this.db.select().from(projects).where(eq12(projects.id, projectId)).get();
7026
+ const project = this.db.select().from(projects).where(eq13(projects.id, projectId)).get();
6841
7027
  if (!project) return;
6842
7028
  for (const notif of notifs) {
6843
7029
  const config = notif.config;
@@ -6867,12 +7053,12 @@ var Notifier = class {
6867
7053
  }
6868
7054
  }
6869
7055
  computeTransitions(runId, projectId) {
6870
- const thisRun = this.db.select().from(runs).where(eq12(runs.id, runId)).get();
7056
+ const thisRun = this.db.select().from(runs).where(eq13(runs.id, runId)).get();
6871
7057
  if (!thisRun) return [];
6872
7058
  const groupSiblings = this.db.select().from(runs).where(and11(
6873
- eq12(runs.projectId, projectId),
6874
- eq12(runs.kind, thisRun.kind),
6875
- eq12(runs.createdAt, thisRun.createdAt)
7059
+ eq13(runs.projectId, projectId),
7060
+ eq13(runs.kind, thisRun.kind),
7061
+ eq13(runs.createdAt, thisRun.createdAt)
6876
7062
  )).all();
6877
7063
  const stillPending = groupSiblings.some((r) => r.status === "queued" || r.status === "running");
6878
7064
  if (stillPending) return [];
@@ -6888,7 +7074,7 @@ var Notifier = class {
6888
7074
  return candidate.id > best.id ? candidate : best;
6889
7075
  });
6890
7076
  if (winner.id !== runId) return [];
6891
- const projectLocations = this.db.select({ locations: projects.locations }).from(projects).where(eq12(projects.id, projectId)).get();
7077
+ const projectLocations = this.db.select({ locations: projects.locations }).from(projects).where(eq13(projects.id, projectId)).get();
6892
7078
  const locationCount = Math.max(
6893
7079
  1,
6894
7080
  (projectLocations?.locations ?? []).length
@@ -6896,9 +7082,9 @@ var Notifier = class {
6896
7082
  const RECENT_FETCH_LIMIT = Math.max(8, locationCount * 4);
6897
7083
  const recentRuns = this.db.select().from(runs).where(
6898
7084
  and11(
6899
- eq12(runs.projectId, projectId),
6900
- eq12(runs.kind, thisRun.kind),
6901
- or(eq12(runs.status, "completed"), eq12(runs.status, "partial"))
7085
+ eq13(runs.projectId, projectId),
7086
+ eq13(runs.kind, thisRun.kind),
7087
+ or(eq13(runs.status, "completed"), eq13(runs.status, "partial"))
6902
7088
  )
6903
7089
  ).orderBy(desc5(runs.createdAt), desc5(runs.id)).limit(RECENT_FETCH_LIMIT).all();
6904
7090
  const groups = groupRunsByCreatedAt(recentRuns);
@@ -6915,13 +7101,13 @@ var Notifier = class {
6915
7101
  provider: querySnapshots.provider,
6916
7102
  location: querySnapshots.location,
6917
7103
  citationState: querySnapshots.citationState
6918
- }).from(querySnapshots).leftJoin(queries, eq12(querySnapshots.queryId, queries.id)).where(inArray5(querySnapshots.runId, currentRunIds)).all();
7104
+ }).from(querySnapshots).leftJoin(queries, eq13(querySnapshots.queryId, queries.id)).where(inArray6(querySnapshots.runId, currentRunIds)).all();
6919
7105
  const previousSnapshots = this.db.select({
6920
7106
  queryId: querySnapshots.queryId,
6921
7107
  provider: querySnapshots.provider,
6922
7108
  location: querySnapshots.location,
6923
7109
  citationState: querySnapshots.citationState
6924
- }).from(querySnapshots).where(inArray5(querySnapshots.runId, previousRunIds)).all();
7110
+ }).from(querySnapshots).where(inArray6(querySnapshots.runId, previousRunIds)).all();
6925
7111
  const prevMap = /* @__PURE__ */ new Map();
6926
7112
  for (const s of previousSnapshots) {
6927
7113
  if (s.queryId == null) continue;
@@ -6948,23 +7134,23 @@ var Notifier = class {
6948
7134
  const targetLabel = redactNotificationUrl(url).urlDisplay;
6949
7135
  const targetCheck = await resolveWebhookTarget(url);
6950
7136
  if (!targetCheck.ok) {
6951
- log13.error("webhook.ssrf-blocked", { url: targetLabel, reason: targetCheck.message });
7137
+ log14.error("webhook.ssrf-blocked", { url: targetLabel, reason: targetCheck.message });
6952
7138
  this.logDelivery(projectId, notificationId, payload.event, "failed", `SSRF: ${targetCheck.message}`);
6953
7139
  return;
6954
7140
  }
6955
- log13.info("webhook.send", { event: payload.event, url: targetLabel });
7141
+ log14.info("webhook.send", { event: payload.event, url: targetLabel });
6956
7142
  const maxRetries = 3;
6957
7143
  const delays = [1e3, 4e3, 16e3];
6958
7144
  for (let attempt = 0; attempt < maxRetries; attempt++) {
6959
7145
  try {
6960
7146
  const response = await deliverWebhook(targetCheck.target, payload, webhookSecret);
6961
7147
  if (response.status >= 200 && response.status < 300) {
6962
- log13.info("webhook.delivered", { event: payload.event, url: targetLabel, httpStatus: response.status });
7148
+ log14.info("webhook.delivered", { event: payload.event, url: targetLabel, httpStatus: response.status });
6963
7149
  this.logDelivery(projectId, notificationId, payload.event, "sent", null);
6964
7150
  return;
6965
7151
  }
6966
7152
  const errorDetail = response.error ?? `HTTP ${response.status}`;
6967
- log13.warn("webhook.attempt-failed", { event: payload.event, url: targetLabel, attempt: attempt + 1, maxRetries, httpStatus: response.status, error: errorDetail });
7153
+ log14.warn("webhook.attempt-failed", { event: payload.event, url: targetLabel, attempt: attempt + 1, maxRetries, httpStatus: response.status, error: errorDetail });
6968
7154
  if (attempt === maxRetries - 1) {
6969
7155
  this.logDelivery(projectId, notificationId, payload.event, "failed", errorDetail);
6970
7156
  }
@@ -6972,7 +7158,7 @@ var Notifier = class {
6972
7158
  const errorDetail = err instanceof Error ? err.message : String(err);
6973
7159
  if (attempt === maxRetries - 1) {
6974
7160
  this.logDelivery(projectId, notificationId, payload.event, "failed", errorDetail);
6975
- log13.error("webhook.exhausted", { event: payload.event, url: targetLabel, maxRetries, error: errorDetail });
7161
+ log14.error("webhook.exhausted", { event: payload.event, url: targetLabel, maxRetries, error: errorDetail });
6976
7162
  }
6977
7163
  }
6978
7164
  if (attempt < maxRetries - 1) {
@@ -6982,7 +7168,7 @@ var Notifier = class {
6982
7168
  }
6983
7169
  logDelivery(projectId, notificationId, event, status, error) {
6984
7170
  this.db.insert(auditLog).values({
6985
- id: crypto14.randomUUID(),
7171
+ id: crypto15.randomUUID(),
6986
7172
  projectId,
6987
7173
  actor: "scheduler",
6988
7174
  action: `notification.${status}`,
@@ -6995,8 +7181,8 @@ var Notifier = class {
6995
7181
  };
6996
7182
 
6997
7183
  // src/run-coordinator.ts
6998
- import { eq as eq13 } from "drizzle-orm";
6999
- var log14 = createLogger("RunCoordinator");
7184
+ import { eq as eq14 } from "drizzle-orm";
7185
+ var log15 = createLogger("RunCoordinator");
7000
7186
  var RunCoordinator = class {
7001
7187
  constructor(db, notifier, intelligenceService, onInsightsGenerated, onAeroEvent) {
7002
7188
  this.db = db;
@@ -7006,10 +7192,10 @@ var RunCoordinator = class {
7006
7192
  this.onAeroEvent = onAeroEvent;
7007
7193
  }
7008
7194
  async onRunCompleted(runId, projectId) {
7009
- const runRow = this.db.select().from(runs).where(eq13(runs.id, runId)).get();
7195
+ const runRow = this.db.select().from(runs).where(eq14(runs.id, runId)).get();
7010
7196
  const kind = runRow?.kind ?? RunKinds["answer-visibility"];
7011
7197
  if (runRow?.trigger === RunTriggers.probe) {
7012
- log14.info("probe.skip-side-effects", { runId, projectId, kind });
7198
+ log15.info("probe.skip-side-effects", { runId, projectId, kind });
7013
7199
  return;
7014
7200
  }
7015
7201
  let insightCount = 0;
@@ -7026,12 +7212,12 @@ var RunCoordinator = class {
7026
7212
  try {
7027
7213
  await this.onInsightsGenerated(runId, projectId, result);
7028
7214
  } catch (err) {
7029
- log14.error("insight-webhook.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7215
+ log15.error("insight-webhook.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7030
7216
  }
7031
7217
  }
7032
7218
  }
7033
7219
  } catch (err) {
7034
- log14.error("intelligence.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7220
+ log15.error("intelligence.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7035
7221
  }
7036
7222
  } else if (kind === RunKinds["gbp-sync"]) {
7037
7223
  try {
@@ -7044,17 +7230,17 @@ var RunCoordinator = class {
7044
7230
  try {
7045
7231
  await this.onInsightsGenerated(runId, projectId, analysisResultFromInsights(gbpInsights));
7046
7232
  } catch (err) {
7047
- log14.error("gbp-insight-webhook.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7233
+ log15.error("gbp-insight-webhook.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7048
7234
  }
7049
7235
  }
7050
7236
  } catch (err) {
7051
- log14.error("gbp-intelligence.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7237
+ log15.error("gbp-intelligence.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7052
7238
  }
7053
7239
  }
7054
7240
  try {
7055
7241
  await this.notifier.onRunCompleted(runId, projectId);
7056
7242
  } catch (err) {
7057
- log14.error("notifier.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7243
+ log15.error("notifier.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7058
7244
  }
7059
7245
  if (this.onAeroEvent) {
7060
7246
  try {
@@ -7067,7 +7253,7 @@ var RunCoordinator = class {
7067
7253
  };
7068
7254
  await this.onAeroEvent(ctx);
7069
7255
  } catch (err) {
7070
- log14.error("aero.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7256
+ log15.error("aero.failed", { runId, error: err instanceof Error ? err.message : String(err) });
7071
7257
  }
7072
7258
  }
7073
7259
  }
@@ -7082,7 +7268,7 @@ var RunCoordinator = class {
7082
7268
  * so the Aero queue is never starved of a follow-up.
7083
7269
  */
7084
7270
  buildDiscoveryAeroContext(runId, projectId, status, error) {
7085
- const session = this.db.select().from(discoverySessions).where(eq13(discoverySessions.runId, runId)).get();
7271
+ const session = this.db.select().from(discoverySessions).where(eq14(discoverySessions.runId, runId)).get();
7086
7272
  const competitorMap = session ? session.competitorMap : [];
7087
7273
  return {
7088
7274
  kind: RunKinds["aeo-discover-probe"],
@@ -7122,8 +7308,8 @@ function analysisResultFromInsights(insights2) {
7122
7308
  }
7123
7309
 
7124
7310
  // src/agent/session-registry.ts
7125
- import crypto16 from "crypto";
7126
- import { eq as eq15 } from "drizzle-orm";
7311
+ import crypto17 from "crypto";
7312
+ import { eq as eq16 } from "drizzle-orm";
7127
7313
 
7128
7314
  // src/agent/session.ts
7129
7315
  import fs7 from "fs";
@@ -7511,8 +7697,8 @@ function resolveSessionProviderAndModel(config, opts) {
7511
7697
  }
7512
7698
 
7513
7699
  // src/agent/memory-store.ts
7514
- import crypto15 from "crypto";
7515
- import { and as and12, desc as desc6, eq as eq14, like, sql as sql5 } from "drizzle-orm";
7700
+ import crypto16 from "crypto";
7701
+ import { and as and12, desc as desc6, eq as eq15, like, sql as sql5 } from "drizzle-orm";
7516
7702
  var COMPACTION_KEY_PREFIX = "compaction:";
7517
7703
  var COMPACTION_NOTES_PER_SESSION = 3;
7518
7704
  function rowToDto(row) {
@@ -7526,7 +7712,7 @@ function rowToDto(row) {
7526
7712
  };
7527
7713
  }
7528
7714
  function listMemoryEntries(db, projectId, opts = {}) {
7529
- const query = db.select().from(agentMemory).where(eq14(agentMemory.projectId, projectId)).orderBy(desc6(agentMemory.updatedAt));
7715
+ const query = db.select().from(agentMemory).where(eq15(agentMemory.projectId, projectId)).orderBy(desc6(agentMemory.updatedAt));
7530
7716
  const rows = opts.limit === void 0 ? query.all() : query.limit(opts.limit).all();
7531
7717
  return rows.map(rowToDto);
7532
7718
  }
@@ -7540,7 +7726,7 @@ function upsertMemoryEntry(db, args) {
7540
7726
  throw new Error(`memory key prefix "${COMPACTION_KEY_PREFIX}" is reserved for compaction notes`);
7541
7727
  }
7542
7728
  const now = (/* @__PURE__ */ new Date()).toISOString();
7543
- const id = crypto15.randomUUID();
7729
+ const id = crypto16.randomUUID();
7544
7730
  db.insert(agentMemory).values({
7545
7731
  id,
7546
7732
  projectId: args.projectId,
@@ -7557,12 +7743,12 @@ function upsertMemoryEntry(db, args) {
7557
7743
  updatedAt: now
7558
7744
  }
7559
7745
  }).run();
7560
- const row = db.select().from(agentMemory).where(and12(eq14(agentMemory.projectId, args.projectId), eq14(agentMemory.key, args.key))).get();
7746
+ const row = db.select().from(agentMemory).where(and12(eq15(agentMemory.projectId, args.projectId), eq15(agentMemory.key, args.key))).get();
7561
7747
  if (!row) throw new Error("memory upsert produced no row");
7562
7748
  return rowToDto(row);
7563
7749
  }
7564
7750
  function deleteMemoryEntry(db, projectId, key) {
7565
- const result = db.delete(agentMemory).where(and12(eq14(agentMemory.projectId, projectId), eq14(agentMemory.key, key))).run();
7751
+ const result = db.delete(agentMemory).where(and12(eq15(agentMemory.projectId, projectId), eq15(agentMemory.key, key))).run();
7566
7752
  const changes = result.changes ?? 0;
7567
7753
  return changes > 0;
7568
7754
  }
@@ -7577,7 +7763,7 @@ function writeCompactionNote(db, args) {
7577
7763
  }
7578
7764
  const now = (/* @__PURE__ */ new Date()).toISOString();
7579
7765
  const key = `${COMPACTION_KEY_PREFIX}${args.sessionId}:${now}`;
7580
- const id = crypto15.randomUUID();
7766
+ const id = crypto16.randomUUID();
7581
7767
  let inserted;
7582
7768
  db.transaction((tx) => {
7583
7769
  tx.insert(agentMemory).values({
@@ -7592,7 +7778,7 @@ function writeCompactionNote(db, args) {
7592
7778
  const sessionPrefix = `${COMPACTION_KEY_PREFIX}${args.sessionId}:`;
7593
7779
  const existing = tx.select({ id: agentMemory.id, updatedAt: agentMemory.updatedAt }).from(agentMemory).where(
7594
7780
  and12(
7595
- eq14(agentMemory.projectId, args.projectId),
7781
+ eq15(agentMemory.projectId, args.projectId),
7596
7782
  like(agentMemory.key, `${sessionPrefix}%`)
7597
7783
  )
7598
7784
  ).orderBy(desc6(agentMemory.updatedAt)).all();
@@ -7600,7 +7786,7 @@ function writeCompactionNote(db, args) {
7600
7786
  if (stale.length > 0) {
7601
7787
  tx.delete(agentMemory).where(sql5`${agentMemory.id} IN (${sql5.join(stale.map((s) => sql5`${s}`), sql5`, `)})`).run();
7602
7788
  }
7603
- const row = tx.select().from(agentMemory).where(and12(eq14(agentMemory.projectId, args.projectId), eq14(agentMemory.key, key))).get();
7789
+ const row = tx.select().from(agentMemory).where(and12(eq15(agentMemory.projectId, args.projectId), eq15(agentMemory.key, key))).get();
7604
7790
  if (row) inserted = rowToDto(row);
7605
7791
  });
7606
7792
  if (!inserted) throw new Error("compaction note write produced no row");
@@ -7733,7 +7919,7 @@ async function compactMessages(args) {
7733
7919
  }
7734
7920
 
7735
7921
  // src/agent/session-registry.ts
7736
- var log15 = createLogger("SessionRegistry");
7922
+ var log16 = createLogger("SessionRegistry");
7737
7923
  var MAX_HYDRATE_NOTES = 20;
7738
7924
  var MAX_HYDRATE_BYTES = 32 * 1024;
7739
7925
  function escapeMemoryFragment(value) {
@@ -7782,7 +7968,7 @@ var SessionRegistry = class {
7782
7968
  modelProvider: effectiveProvider,
7783
7969
  modelId: effectiveModelId,
7784
7970
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
7785
- }).where(eq15(agentSessions.projectId, projectId)).run();
7971
+ }).where(eq16(agentSessions.projectId, projectId)).run();
7786
7972
  }
7787
7973
  const agent2 = createAeroSession({
7788
7974
  projectName,
@@ -7960,13 +8146,13 @@ ${lines.join("\n")}
7960
8146
  agent.state.messages = result.messages;
7961
8147
  agent.state.systemPrompt = this.buildHydratedSystemPrompt(projectId, row.systemPrompt);
7962
8148
  this.save(projectName);
7963
- log15.info("compaction.completed", {
8149
+ log16.info("compaction.completed", {
7964
8150
  projectName,
7965
8151
  removedCount: result.removedCount,
7966
8152
  summaryBytes: Buffer.byteLength(result.summary, "utf8")
7967
8153
  });
7968
8154
  } catch (err) {
7969
- log15.error("compaction.failed", {
8155
+ log16.error("compaction.failed", {
7970
8156
  projectName,
7971
8157
  error: err instanceof Error ? err.message : String(err)
7972
8158
  });
@@ -7996,7 +8182,7 @@ ${lines.join("\n")}
7996
8182
  modelProvider: nextProvider,
7997
8183
  modelId: nextModelId,
7998
8184
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
7999
- }).where(eq15(agentSessions.projectId, projectId)).run();
8185
+ }).where(eq16(agentSessions.projectId, projectId)).run();
8000
8186
  }
8001
8187
  /** Persist a session's transcript back to the DB. Call after any run settles. */
8002
8188
  save(projectName) {
@@ -8063,7 +8249,7 @@ ${lines.join("\n")}
8063
8249
  await agent.prompt(msgs);
8064
8250
  this.save(projectName);
8065
8251
  } catch (err) {
8066
- log15.error("drain.failed", {
8252
+ log16.error("drain.failed", {
8067
8253
  projectName,
8068
8254
  error: err instanceof Error ? err.message : String(err)
8069
8255
  });
@@ -8158,17 +8344,17 @@ ${lines.join("\n")}
8158
8344
  return id;
8159
8345
  }
8160
8346
  tryResolveProjectId(projectName) {
8161
- const row = this.opts.db.select({ id: projects.id }).from(projects).where(eq15(projects.name, projectName)).get();
8347
+ const row = this.opts.db.select({ id: projects.id }).from(projects).where(eq16(projects.name, projectName)).get();
8162
8348
  return row?.id;
8163
8349
  }
8164
8350
  loadRow(projectId) {
8165
- const row = this.opts.db.select().from(agentSessions).where(eq15(agentSessions.projectId, projectId)).get();
8351
+ const row = this.opts.db.select().from(agentSessions).where(eq16(agentSessions.projectId, projectId)).get();
8166
8352
  return row ?? null;
8167
8353
  }
8168
8354
  insertRow(params) {
8169
8355
  const now = (/* @__PURE__ */ new Date()).toISOString();
8170
8356
  this.opts.db.insert(agentSessions).values({
8171
- id: crypto16.randomUUID(),
8357
+ id: crypto17.randomUUID(),
8172
8358
  projectId: params.projectId,
8173
8359
  systemPrompt: params.systemPrompt,
8174
8360
  modelProvider: params.provider ?? params.modelProvider ?? AgentProviderIds.claude,
@@ -8181,14 +8367,14 @@ ${lines.join("\n")}
8181
8367
  }
8182
8368
  updateRow(projectId, patch) {
8183
8369
  const now = (/* @__PURE__ */ new Date()).toISOString();
8184
- this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(eq15(agentSessions.projectId, projectId)).run();
8370
+ this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(eq16(agentSessions.projectId, projectId)).run();
8185
8371
  }
8186
8372
  };
8187
8373
 
8188
8374
  // src/agent/agent-routes.ts
8189
- import { eq as eq16 } from "drizzle-orm";
8375
+ import { eq as eq17 } from "drizzle-orm";
8190
8376
  function resolveProject(db, name) {
8191
- const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(eq16(projects.name, name)).get();
8377
+ const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(eq17(projects.name, name)).get();
8192
8378
  if (!row) throw notFound("project", name);
8193
8379
  return row;
8194
8380
  }
@@ -8197,7 +8383,7 @@ function registerAgentRoutes(app, opts) {
8197
8383
  "/projects/:name/agent/transcript",
8198
8384
  async (request) => {
8199
8385
  const project = resolveProject(opts.db, request.params.name);
8200
- const row = opts.db.select().from(agentSessions).where(eq16(agentSessions.projectId, project.id)).get();
8386
+ const row = opts.db.select().from(agentSessions).where(eq17(agentSessions.projectId, project.id)).get();
8201
8387
  if (!row) {
8202
8388
  return { messages: [], modelProvider: null, modelId: null, updatedAt: null };
8203
8389
  }
@@ -8221,7 +8407,7 @@ function registerAgentRoutes(app, opts) {
8221
8407
  async (request) => {
8222
8408
  const project = resolveProject(opts.db, request.params.name);
8223
8409
  opts.sessionRegistry.reset(project.name);
8224
- opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq16(agentSessions.projectId, project.id)).run();
8410
+ opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq17(agentSessions.projectId, project.id)).run();
8225
8411
  return { status: "reset" };
8226
8412
  }
8227
8413
  );
@@ -8661,7 +8847,7 @@ function formatAuditFactorScore(factor) {
8661
8847
  }
8662
8848
 
8663
8849
  // src/snapshot-service.ts
8664
- var log16 = createLogger("Snapshot");
8850
+ var log17 = createLogger("Snapshot");
8665
8851
  var ANALYSIS_PROVIDER_PRIORITY = ["openai", "claude", "gemini", "perplexity", "local"];
8666
8852
  var SNAPSHOT_QUERY_COUNT = 6;
8667
8853
  var ProviderExecutionGate2 = class {
@@ -8804,13 +8990,12 @@ var SnapshotService = class {
8804
8990
  return mapAuditReport(report);
8805
8991
  } catch (err) {
8806
8992
  const message = err instanceof Error ? err.message : String(err);
8807
- log16.warn("audit.failed", { homepageUrl, error: message });
8993
+ log17.warn("audit.failed", { homepageUrl, error: message });
8808
8994
  return {
8809
8995
  url: homepageUrl,
8810
8996
  finalUrl: homepageUrl,
8811
8997
  auditedAt: (/* @__PURE__ */ new Date()).toISOString(),
8812
8998
  overallScore: 0,
8813
- overallGrade: "N/A",
8814
8999
  summary: `Technical audit unavailable: ${message}`,
8815
9000
  factors: []
8816
9001
  };
@@ -8834,7 +9019,7 @@ var SnapshotService = class {
8834
9019
  queries: parsedQueries
8835
9020
  };
8836
9021
  } catch (err) {
8837
- log16.warn("profile.generation-failed", {
9022
+ log17.warn("profile.generation-failed", {
8838
9023
  domain: ctx.domain,
8839
9024
  provider: ctx.analysisProvider.adapter.name,
8840
9025
  error: err instanceof Error ? err.message : String(err)
@@ -8976,7 +9161,7 @@ var SnapshotService = class {
8976
9161
  recommendedActions: uniqueStrings(parsed.recommendedActions ?? []).slice(0, 4)
8977
9162
  };
8978
9163
  } catch (err) {
8979
- log16.warn("response.analysis-failed", {
9164
+ log17.warn("response.analysis-failed", {
8980
9165
  provider: ctx.analysisProvider.adapter.name,
8981
9166
  error: err instanceof Error ? err.message : String(err)
8982
9167
  });
@@ -9036,7 +9221,7 @@ function buildBatchAnalysisPrompt(ctx) {
9036
9221
  `Services: ${ctx.profile.services.join(", ") || "unknown"}`,
9037
9222
  `Category terms: ${ctx.profile.categoryTerms.join(", ") || "unknown"}`,
9038
9223
  `Manual competitor hints: ${ctx.manualCompetitors.join(", ") || "none"}`,
9039
- `Technical audit: ${ctx.audit.overallScore}/100 (${ctx.audit.overallGrade}) \u2014 ${ctx.audit.summary}`,
9224
+ `Technical audit: ${ctx.audit.overallScore}/100 \u2014 ${ctx.audit.summary}`,
9040
9225
  "",
9041
9226
  "Responses JSON:",
9042
9227
  JSON.stringify(ctx.responses, null, 2)
@@ -9047,7 +9232,7 @@ function buildFallbackBatchAssessment(companyName, audit) {
9047
9232
  assessments: [],
9048
9233
  whatThisMeans: [
9049
9234
  `${companyName} needs category-level visibility, not just branded comprehension.`,
9050
- `The technical baseline is ${audit.overallScore}/100 (${audit.overallGrade}), so weak site signals may be making AI systems prefer better-structured alternatives.`
9235
+ `The technical baseline is ${audit.overallScore}/100, so weak site signals may be making AI systems prefer better-structured alternatives.`
9051
9236
  ],
9052
9237
  recommendedActions: buildFallbackRecommendedActions(audit)
9053
9238
  };
@@ -9191,7 +9376,6 @@ function mapAuditReport(report) {
9191
9376
  finalUrl: report.finalUrl,
9192
9377
  auditedAt: report.auditedAt,
9193
9378
  overallScore: report.overallScore,
9194
- overallGrade: report.overallGrade,
9195
9379
  summary: report.summary,
9196
9380
  factors: report.factors.map(mapAuditFactor)
9197
9381
  };
@@ -9202,8 +9386,6 @@ function mapAuditFactor(factor) {
9202
9386
  name: factor.name,
9203
9387
  weight: factor.weight,
9204
9388
  score: factor.score,
9205
- grade: factor.grade,
9206
- status: factor.status,
9207
9389
  findings: factor.findings.map((finding) => ({
9208
9390
  type: finding.type,
9209
9391
  message: finding.message
@@ -9261,7 +9443,7 @@ function clipText(value, length) {
9261
9443
  // src/server.ts
9262
9444
  var _require3 = createRequire3(import.meta.url);
9263
9445
  var { version: PKG_VERSION2 } = _require3("../package.json");
9264
- var log17 = createLogger("Server");
9446
+ var log18 = createLogger("Server");
9265
9447
  var DEFAULT_QUOTA = {
9266
9448
  maxConcurrency: 2,
9267
9449
  maxRequestsPerMinute: 10,
@@ -9291,14 +9473,14 @@ function summarizeProviderConfig(provider, config) {
9291
9473
  };
9292
9474
  }
9293
9475
  function hashApiKey(key) {
9294
- return crypto17.createHash("sha256").update(key).digest("hex");
9476
+ return crypto18.createHash("sha256").update(key).digest("hex");
9295
9477
  }
9296
9478
  var DASHBOARD_SCRYPT_KEYLEN = 64;
9297
9479
  var DASHBOARD_SCRYPT_COST = 1 << 15;
9298
9480
  var DASHBOARD_SCRYPT_MAXMEM = 64 * 1024 * 1024;
9299
9481
  function hashDashboardPassword(password) {
9300
- const salt = crypto17.randomBytes(16);
9301
- const derived = crypto17.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
9482
+ const salt = crypto18.randomBytes(16);
9483
+ const derived = crypto18.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
9302
9484
  N: DASHBOARD_SCRYPT_COST,
9303
9485
  maxmem: DASHBOARD_SCRYPT_MAXMEM
9304
9486
  });
@@ -9319,18 +9501,18 @@ function verifyDashboardPassword(password, storedHash) {
9319
9501
  } catch {
9320
9502
  return { ok: false, needsRehash: false };
9321
9503
  }
9322
- const derived = crypto17.scryptSync(password, salt, expected.length, {
9504
+ const derived = crypto18.scryptSync(password, salt, expected.length, {
9323
9505
  N: DASHBOARD_SCRYPT_COST,
9324
9506
  maxmem: DASHBOARD_SCRYPT_MAXMEM
9325
9507
  });
9326
9508
  if (derived.length !== expected.length) return { ok: false, needsRehash: false };
9327
- return { ok: crypto17.timingSafeEqual(derived, expected), needsRehash: false };
9509
+ return { ok: crypto18.timingSafeEqual(derived, expected), needsRehash: false };
9328
9510
  }
9329
9511
  if (/^[a-f0-9]{64}$/i.test(storedHash)) {
9330
9512
  const candidate = Buffer.from(hashApiKey(password), "hex");
9331
9513
  const expected = Buffer.from(storedHash, "hex");
9332
9514
  if (candidate.length !== expected.length) return { ok: false, needsRehash: false };
9333
- const ok = crypto17.timingSafeEqual(candidate, expected);
9515
+ const ok = crypto18.timingSafeEqual(candidate, expected);
9334
9516
  return { ok, needsRehash: ok };
9335
9517
  }
9336
9518
  return { ok: false, needsRehash: false };
@@ -9389,7 +9571,7 @@ function applyLegacyCredentials(rows, config) {
9389
9571
  }
9390
9572
  if (migratedGoogle > 0) {
9391
9573
  saveConfigPatch({ google: config.google });
9392
- log17.info("credentials.migrated", { type: "google", count: migratedGoogle });
9574
+ log18.info("credentials.migrated", { type: "google", count: migratedGoogle });
9393
9575
  }
9394
9576
  let migratedGa4 = 0;
9395
9577
  for (const row of rows.ga4) {
@@ -9407,7 +9589,7 @@ function applyLegacyCredentials(rows, config) {
9407
9589
  }
9408
9590
  if (migratedGa4 > 0) {
9409
9591
  saveConfigPatch({ ga4: config.ga4 });
9410
- log17.info("credentials.migrated", { type: "ga4", count: migratedGa4 });
9592
+ log18.info("credentials.migrated", { type: "ga4", count: migratedGa4 });
9411
9593
  }
9412
9594
  }
9413
9595
  async function createServer(opts) {
@@ -9439,11 +9621,11 @@ async function createServer(opts) {
9439
9621
  applyLegacyCredentials(legacyRows, opts.config);
9440
9622
  dropLegacyCredentialColumns(opts.db);
9441
9623
  } catch (err) {
9442
- log17.warn("credentials.migration.failed", {
9624
+ log18.warn("credentials.migration.failed", {
9443
9625
  error: err instanceof Error ? err.message : String(err)
9444
9626
  });
9445
9627
  }
9446
- log17.info("providers.configured", { providers: Object.keys(providers).filter((k) => {
9628
+ log18.info("providers.configured", { providers: Object.keys(providers).filter((k) => {
9447
9629
  const p = providers[k];
9448
9630
  return p?.apiKey || p?.baseUrl || p?.vertexProject;
9449
9631
  }) });
@@ -9492,7 +9674,7 @@ async function createServer(opts) {
9492
9674
  intelligenceService,
9493
9675
  (runId, projectId, result) => notifier.dispatchInsightWebhooks(runId, projectId, result),
9494
9676
  async (ctx) => {
9495
- const project = opts.db.select({ name: projects.name }).from(projects).where(eq17(projects.id, ctx.projectId)).get();
9677
+ const project = opts.db.select({ name: projects.name }).from(projects).where(eq18(projects.id, ctx.projectId)).get();
9496
9678
  if (!project) return;
9497
9679
  let content;
9498
9680
  if (ctx.kind === RunKinds["aeo-discover-probe"]) {
@@ -9535,6 +9717,11 @@ async function createServer(opts) {
9535
9717
  app.log.error({ runId, err }, "GBP sync failed");
9536
9718
  });
9537
9719
  };
9720
+ const runSiteAudit = (runId, projectId, auditOpts) => {
9721
+ executeSiteAudit(opts.db, runId, projectId, auditOpts ?? {}).then(() => runCoordinator.onRunCompleted(runId, projectId)).catch((err) => {
9722
+ app.log.error({ runId, err }, "Site audit failed");
9723
+ });
9724
+ };
9538
9725
  const scheduler = new Scheduler(opts.db, {
9539
9726
  onRunCreated: (runId, projectId, providers2, location) => {
9540
9727
  jobRunner.executeRun(runId, projectId, providers2, location).catch((err) => {
@@ -9560,8 +9747,8 @@ async function createServer(opts) {
9560
9747
  });
9561
9748
  if (!probed) return;
9562
9749
  const alreadySynced = opts.db.select().from(ccReleaseSyncs).where(and13(
9563
- eq17(ccReleaseSyncs.release, probed.release),
9564
- eq17(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)
9750
+ eq18(ccReleaseSyncs.release, probed.release),
9751
+ eq18(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)
9565
9752
  )).limit(1).get();
9566
9753
  if (alreadySynced) {
9567
9754
  app.log.info({ projectName, release: probed.release }, "Scheduled backlinks sync: already up to date, skipping");
@@ -9574,6 +9761,9 @@ async function createServer(opts) {
9574
9761
  );
9575
9762
  });
9576
9763
  })();
9764
+ },
9765
+ onSiteAuditRequested: (runId, projectId) => {
9766
+ runSiteAudit(runId, projectId);
9577
9767
  }
9578
9768
  });
9579
9769
  const providerSummary = API_ADAPTERS.map((adapter) => ({
@@ -9691,7 +9881,7 @@ async function createServer(opts) {
9691
9881
  return removed;
9692
9882
  }
9693
9883
  };
9694
- const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto17.randomBytes(32).toString("hex");
9884
+ const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto18.randomBytes(32).toString("hex");
9695
9885
  const googleConnectionStore = {
9696
9886
  listConnections: (domain) => listGoogleConnections(opts.config, domain),
9697
9887
  getConnection: (domain, connectionType) => getGoogleConnection(opts.config, domain, connectionType),
@@ -9737,11 +9927,11 @@ async function createServer(opts) {
9737
9927
  const apiPrefix = basePath ? `${basePath}api/v1` : "/api/v1";
9738
9928
  if (opts.config.apiKey) {
9739
9929
  const keyHash = hashApiKey(opts.config.apiKey);
9740
- const existing = opts.db.select().from(apiKeys).where(eq17(apiKeys.keyHash, keyHash)).get();
9930
+ const existing = opts.db.select().from(apiKeys).where(eq18(apiKeys.keyHash, keyHash)).get();
9741
9931
  if (!existing) {
9742
9932
  const prefix = opts.config.apiKey.slice(0, 12);
9743
9933
  opts.db.insert(apiKeys).values({
9744
- id: `key_${crypto17.randomBytes(8).toString("hex")}`,
9934
+ id: `key_${crypto18.randomBytes(8).toString("hex")}`,
9745
9935
  name: "default",
9746
9936
  keyHash,
9747
9937
  keyPrefix: prefix,
@@ -9765,7 +9955,7 @@ async function createServer(opts) {
9765
9955
  };
9766
9956
  const createSession = (apiKeyId) => {
9767
9957
  pruneExpiredSessions();
9768
- const sessionId = crypto17.randomBytes(32).toString("hex");
9958
+ const sessionId = crypto18.randomBytes(32).toString("hex");
9769
9959
  sessions.set(sessionId, {
9770
9960
  apiKeyId,
9771
9961
  expiresAt: Date.now() + SESSION_TTL_MS
@@ -9789,7 +9979,7 @@ async function createServer(opts) {
9789
9979
  };
9790
9980
  const getDefaultApiKey = () => {
9791
9981
  if (!opts.config.apiKey) return void 0;
9792
- return opts.db.select().from(apiKeys).where(eq17(apiKeys.keyHash, hashApiKey(opts.config.apiKey))).get();
9982
+ return opts.db.select().from(apiKeys).where(eq18(apiKeys.keyHash, hashApiKey(opts.config.apiKey))).get();
9793
9983
  };
9794
9984
  const createPasswordSession = (reply) => {
9795
9985
  const key = getDefaultApiKey();
@@ -9851,12 +10041,12 @@ async function createServer(opts) {
9851
10041
  return reply.send({ authenticated: true });
9852
10042
  }
9853
10043
  if (apiKey) {
9854
- const key = opts.db.select().from(apiKeys).where(eq17(apiKeys.keyHash, hashApiKey(apiKey))).get();
10044
+ const key = opts.db.select().from(apiKeys).where(eq18(apiKeys.keyHash, hashApiKey(apiKey))).get();
9855
10045
  if (!key || key.revokedAt) {
9856
10046
  const err2 = authInvalid();
9857
10047
  return reply.status(err2.statusCode).send(err2.toJSON());
9858
10048
  }
9859
- opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq17(apiKeys.id, key.id)).run();
10049
+ opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq18(apiKeys.id, key.id)).run();
9860
10050
  const sessionId = createSession(key.id);
9861
10051
  reply.header("set-cookie", serializeSessionCookie({
9862
10052
  name: SESSION_COOKIE_NAME,
@@ -10010,7 +10200,7 @@ async function createServer(opts) {
10010
10200
  deps: {
10011
10201
  enqueueAutoExtract: ({ projectId, release: r }) => {
10012
10202
  const now = (/* @__PURE__ */ new Date()).toISOString();
10013
- const runId = crypto17.randomUUID();
10203
+ const runId = crypto18.randomUUID();
10014
10204
  opts.db.insert(runs).values({
10015
10205
  id: runId,
10016
10206
  projectId,
@@ -10048,6 +10238,9 @@ async function createServer(opts) {
10048
10238
  app.log.error({ runId: input.runId, err }, "Discovery run failed");
10049
10239
  });
10050
10240
  },
10241
+ onSiteAuditRequested: (runId, projectId, auditOpts) => {
10242
+ runSiteAudit(runId, projectId, auditOpts);
10243
+ },
10051
10244
  onBacklinksPruneCache: (release) => {
10052
10245
  try {
10053
10246
  pruneCachedRelease(release);
@@ -10093,7 +10286,7 @@ async function createServer(opts) {
10093
10286
  ...inspectOpts,
10094
10287
  config: opts.config
10095
10288
  }).then(() => {
10096
- const finished = opts.db.select({ status: runs.status }).from(runs).where(eq17(runs.id, runId)).get();
10289
+ const finished = opts.db.select({ status: runs.status }).from(runs).where(eq18(runs.id, runId)).get();
10097
10290
  if (finished?.status === RunStatuses.completed || finished?.status === RunStatuses.partial) {
10098
10291
  return maybeRefreshGscCoverage(opts.db, opts.config, projectId);
10099
10292
  }
@@ -10181,7 +10374,7 @@ async function createServer(opts) {
10181
10374
  const targetProjectIds = affectedProjectIds.length > 0 ? affectedProjectIds : [null];
10182
10375
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
10183
10376
  opts.db.insert(auditLog).values(targetProjectIds.map((projectId) => ({
10184
- id: crypto17.randomUUID(),
10377
+ id: crypto18.randomUUID(),
10185
10378
  projectId,
10186
10379
  actor: "api",
10187
10380
  action: existing ? "provider.updated" : "provider.created",