@ainyc/canonry 4.47.0 → 4.51.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.
- package/assets/agent-workspace/skills/aero/SKILL.md +11 -0
- package/assets/agent-workspace/skills/aero/references/orchestration.md +8 -0
- package/assets/agent-workspace/skills/aero/soul.md +1 -1
- package/assets/agent-workspace/skills/canonry/SKILL.md +2 -0
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +14 -1
- package/assets/assets/BacklinksPage-DIZCcqsP.js +1 -0
- package/assets/assets/ChartPrimitives-9Kx3gzQL.js +1 -0
- package/assets/assets/ProjectPage-R2cxJb5Y.js +6 -0
- package/assets/assets/RunRow-DqezNIUy.js +1 -0
- package/assets/assets/RunsPage-CfvTJ9Ny.js +1 -0
- package/assets/assets/SettingsPage-HfMGIa5v.js +1 -0
- package/assets/assets/TrafficPage-DV_Dvpl3.js +1 -0
- package/assets/assets/TrafficSourceDetailPage-lefreKBO.js +1 -0
- package/assets/assets/arrow-left-DpxpMUNt.js +1 -0
- package/assets/assets/index-DKBPD33e.js +210 -0
- package/assets/assets/{index-BDMNXVHa.css → index-DeGyEwik.css} +1 -1
- package/assets/assets/server-traffic-Bm8iKtXK.js +1 -0
- package/assets/assets/trash-2-CnBiLbiZ.js +1 -0
- package/assets/assets/vendor-markdown-DK7fbRNb.js +14 -0
- package/assets/assets/vendor-radix-B57xfQbP.js +45 -0
- package/assets/assets/vendor-recharts-DWvKDyBF.js +36 -0
- package/assets/assets/vendor-tanstack-Dq7p98wZ.js +1 -0
- package/assets/index.html +6 -2
- package/dist/{chunk-4WXY57ET.js → chunk-2ARCCG5E.js} +1652 -753
- package/dist/{chunk-M7MSNUNQ.js → chunk-DLDLDWH4.js} +142 -44
- package/dist/{chunk-WYBKCDUH.js → chunk-FDR3G6SB.js} +2780 -2018
- package/dist/chunk-GGXU5VKI.js +5778 -0
- package/dist/cli.js +17 -8
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-ADZRFCGO.js → intelligence-service-XMZEWLCW.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +7 -6
- package/assets/assets/index-CPUAzk7n.js +0 -302
- package/dist/chunk-ON545FBK.js +0 -2369
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
CitationStates,
|
|
3
3
|
ContentActions,
|
|
4
4
|
RunKinds,
|
|
5
|
+
RunTriggers,
|
|
5
6
|
__export,
|
|
6
7
|
brandKeyFromText,
|
|
7
8
|
brandLabelFromDomain,
|
|
@@ -9,10 +10,10 @@ import {
|
|
|
9
10
|
categoryLabel,
|
|
10
11
|
determineAnswerMentioned,
|
|
11
12
|
normalizeProjectDomain
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-2ARCCG5E.js";
|
|
13
14
|
|
|
14
15
|
// src/intelligence-service.ts
|
|
15
|
-
import { eq, desc, asc, and, or, inArray } from "drizzle-orm";
|
|
16
|
+
import { eq, desc, asc, and, ne, or, inArray } from "drizzle-orm";
|
|
16
17
|
|
|
17
18
|
// ../db/src/client.ts
|
|
18
19
|
import { mkdirSync } from "fs";
|
|
@@ -36,6 +37,7 @@ __export(schema_exports, {
|
|
|
36
37
|
bingUrlInspections: () => bingUrlInspections,
|
|
37
38
|
ccReleaseSyncs: () => ccReleaseSyncs,
|
|
38
39
|
competitors: () => competitors,
|
|
40
|
+
contentTargetDismissals: () => contentTargetDismissals,
|
|
39
41
|
crawlerEventsHourly: () => crawlerEventsHourly,
|
|
40
42
|
discoveryProbes: () => discoveryProbes,
|
|
41
43
|
discoverySessions: () => discoverySessions,
|
|
@@ -57,6 +59,7 @@ __export(schema_exports, {
|
|
|
57
59
|
queries: () => queries,
|
|
58
60
|
querySnapshots: () => querySnapshots,
|
|
59
61
|
rawEventSamples: () => rawEventSamples,
|
|
62
|
+
recommendationExplanations: () => recommendationExplanations,
|
|
60
63
|
runs: () => runs,
|
|
61
64
|
schedules: () => schedules,
|
|
62
65
|
trafficSources: () => trafficSources,
|
|
@@ -68,16 +71,16 @@ var projects = sqliteTable("projects", {
|
|
|
68
71
|
name: text("name").notNull().unique(),
|
|
69
72
|
displayName: text("display_name").notNull(),
|
|
70
73
|
canonicalDomain: text("canonical_domain").notNull(),
|
|
71
|
-
ownedDomains: text("owned_domains").notNull().default(
|
|
72
|
-
aliases: text("aliases").notNull().default(
|
|
74
|
+
ownedDomains: text("owned_domains", { mode: "json" }).$type().notNull().default([]),
|
|
75
|
+
aliases: text("aliases", { mode: "json" }).$type().notNull().default([]),
|
|
73
76
|
country: text("country").notNull(),
|
|
74
77
|
language: text("language").notNull(),
|
|
75
|
-
tags: text("tags").notNull().default(
|
|
76
|
-
labels: text("labels").notNull().default(
|
|
77
|
-
providers: text("providers").notNull().default(
|
|
78
|
-
locations: text("locations").notNull().default(
|
|
78
|
+
tags: text("tags", { mode: "json" }).$type().notNull().default([]),
|
|
79
|
+
labels: text("labels", { mode: "json" }).$type().notNull().default({}),
|
|
80
|
+
providers: text("providers", { mode: "json" }).$type().notNull().default([]),
|
|
81
|
+
locations: text("locations", { mode: "json" }).$type().notNull().default([]),
|
|
79
82
|
defaultLocation: text("default_location"),
|
|
80
|
-
autoExtractBacklinks: integer("auto_extract_backlinks").notNull().default(
|
|
83
|
+
autoExtractBacklinks: integer("auto_extract_backlinks", { mode: "boolean" }).notNull().default(false),
|
|
81
84
|
configSource: text("config_source").notNull().default("cli"),
|
|
82
85
|
configRevision: integer("config_revision").notNull().default(1),
|
|
83
86
|
icpDescription: text("icp_description"),
|
|
@@ -111,7 +114,7 @@ var runs = sqliteTable("runs", {
|
|
|
111
114
|
status: text("status").notNull().default("queued"),
|
|
112
115
|
trigger: text("trigger").notNull().default("manual"),
|
|
113
116
|
location: text("location"),
|
|
114
|
-
queries: text("queries"),
|
|
117
|
+
queries: text("queries", { mode: "json" }).$type(),
|
|
115
118
|
sourceId: text("source_id"),
|
|
116
119
|
startedAt: text("started_at"),
|
|
117
120
|
finishedAt: text("finished_at"),
|
|
@@ -138,9 +141,9 @@ var querySnapshots = sqliteTable("query_snapshots", {
|
|
|
138
141
|
citationState: text("citation_state").notNull(),
|
|
139
142
|
answerMentioned: integer("answer_mentioned", { mode: "boolean" }),
|
|
140
143
|
answerText: text("answer_text"),
|
|
141
|
-
citedDomains: text("cited_domains").notNull().default(
|
|
142
|
-
competitorOverlap: text("competitor_overlap").notNull().default(
|
|
143
|
-
recommendedCompetitors: text("recommended_competitors").notNull().default(
|
|
144
|
+
citedDomains: text("cited_domains", { mode: "json" }).$type().notNull().default([]),
|
|
145
|
+
competitorOverlap: text("competitor_overlap", { mode: "json" }).$type().notNull().default([]),
|
|
146
|
+
recommendedCompetitors: text("recommended_competitors", { mode: "json" }).$type().notNull().default([]),
|
|
144
147
|
location: text("location"),
|
|
145
148
|
screenshotPath: text("screenshot_path"),
|
|
146
149
|
rawResponse: text("raw_response"),
|
|
@@ -176,7 +179,7 @@ var apiKeys = sqliteTable("api_keys", {
|
|
|
176
179
|
name: text("name").notNull(),
|
|
177
180
|
keyHash: text("key_hash").notNull().unique(),
|
|
178
181
|
keyPrefix: text("key_prefix").notNull(),
|
|
179
|
-
scopes: text("scopes").notNull().default(
|
|
182
|
+
scopes: text("scopes", { mode: "json" }).$type().notNull().default(["*"]),
|
|
180
183
|
createdAt: text("created_at").notNull(),
|
|
181
184
|
lastUsedAt: text("last_used_at"),
|
|
182
185
|
revokedAt: text("revoked_at")
|
|
@@ -194,8 +197,8 @@ var schedules = sqliteTable("schedules", {
|
|
|
194
197
|
cronExpr: text("cron_expr").notNull(),
|
|
195
198
|
preset: text("preset"),
|
|
196
199
|
timezone: text("timezone").notNull().default("UTC"),
|
|
197
|
-
enabled: integer("enabled").notNull().default(
|
|
198
|
-
providers: text("providers").notNull().default(
|
|
200
|
+
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
|
201
|
+
providers: text("providers", { mode: "json" }).$type().notNull().default([]),
|
|
199
202
|
/** Optional traffic-source UUID for traffic-sync schedules. Null for other kinds. */
|
|
200
203
|
sourceId: text("source_id"),
|
|
201
204
|
lastRunAt: text("last_run_at"),
|
|
@@ -209,9 +212,9 @@ var notifications = sqliteTable("notifications", {
|
|
|
209
212
|
id: text("id").primaryKey(),
|
|
210
213
|
projectId: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
|
|
211
214
|
channel: text("channel").notNull(),
|
|
212
|
-
config: text("config").notNull(),
|
|
215
|
+
config: text("config", { mode: "json" }).$type().notNull(),
|
|
213
216
|
webhookSecret: text("webhook_secret"),
|
|
214
|
-
enabled: integer("enabled").notNull().default(
|
|
217
|
+
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
|
215
218
|
createdAt: text("created_at").notNull(),
|
|
216
219
|
updatedAt: text("updated_at").notNull()
|
|
217
220
|
}, (table) => [
|
|
@@ -223,7 +226,7 @@ var googleConnections = sqliteTable("google_connections", {
|
|
|
223
226
|
connectionType: text("connection_type").notNull(),
|
|
224
227
|
propertyId: text("property_id"),
|
|
225
228
|
sitemapUrl: text("sitemap_url"),
|
|
226
|
-
scopes: text("scopes").notNull().default(
|
|
229
|
+
scopes: text("scopes", { mode: "json" }).$type().notNull().default([]),
|
|
227
230
|
createdAt: text("created_at").notNull(),
|
|
228
231
|
updatedAt: text("updated_at").notNull()
|
|
229
232
|
}, (table) => [
|
|
@@ -260,9 +263,9 @@ var gscUrlInspections = sqliteTable("gsc_url_inspections", {
|
|
|
260
263
|
robotsTxtState: text("robots_txt_state"),
|
|
261
264
|
crawlTime: text("crawl_time"),
|
|
262
265
|
lastCrawlResult: text("last_crawl_result"),
|
|
263
|
-
isMobileFriendly: integer("is_mobile_friendly"),
|
|
264
|
-
richResults: text("rich_results").notNull().default(
|
|
265
|
-
referringUrls: text("referring_urls").notNull().default(
|
|
266
|
+
isMobileFriendly: integer("is_mobile_friendly", { mode: "boolean" }),
|
|
267
|
+
richResults: text("rich_results", { mode: "json" }).$type().notNull().default([]),
|
|
268
|
+
referringUrls: text("referring_urls", { mode: "json" }).$type().notNull().default([]),
|
|
266
269
|
inspectedAt: text("inspected_at").notNull(),
|
|
267
270
|
createdAt: text("created_at").notNull()
|
|
268
271
|
}, (table) => [
|
|
@@ -277,7 +280,7 @@ var gscCoverageSnapshots = sqliteTable("gsc_coverage_snapshots", {
|
|
|
277
280
|
date: text("date").notNull(),
|
|
278
281
|
indexed: integer("indexed").notNull().default(0),
|
|
279
282
|
notIndexed: integer("not_indexed").notNull().default(0),
|
|
280
|
-
reasonBreakdown: text("reason_breakdown").notNull().default(
|
|
283
|
+
reasonBreakdown: text("reason_breakdown", { mode: "json" }).$type().notNull().default({}),
|
|
281
284
|
createdAt: text("created_at").notNull()
|
|
282
285
|
}, (table) => [
|
|
283
286
|
index("idx_gsc_coverage_snap_project_date").on(table.projectId, table.date),
|
|
@@ -310,7 +313,7 @@ var bingUrlInspections = sqliteTable("bing_url_inspections", {
|
|
|
310
313
|
projectId: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
|
|
311
314
|
url: text("url").notNull(),
|
|
312
315
|
httpCode: integer("http_code"),
|
|
313
|
-
inIndex: integer("in_index"),
|
|
316
|
+
inIndex: integer("in_index", { mode: "boolean" }),
|
|
314
317
|
lastCrawledDate: text("last_crawled_date"),
|
|
315
318
|
inIndexDate: text("in_index_date"),
|
|
316
319
|
inspectedAt: text("inspected_at").notNull(),
|
|
@@ -471,8 +474,8 @@ var insights = sqliteTable("insights", {
|
|
|
471
474
|
title: text("title").notNull(),
|
|
472
475
|
query: text("query").notNull(),
|
|
473
476
|
provider: text("provider").notNull(),
|
|
474
|
-
recommendation: text("recommendation"),
|
|
475
|
-
cause: text("cause"),
|
|
477
|
+
recommendation: text("recommendation", { mode: "json" }).$type(),
|
|
478
|
+
cause: text("cause", { mode: "json" }).$type(),
|
|
476
479
|
dismissed: integer("dismissed", { mode: "boolean" }).notNull().default(false),
|
|
477
480
|
createdAt: text("created_at").notNull()
|
|
478
481
|
}, (table) => [
|
|
@@ -488,7 +491,7 @@ var healthSnapshots = sqliteTable("health_snapshots", {
|
|
|
488
491
|
overallCitedRate: text("overall_cited_rate").notNull(),
|
|
489
492
|
totalPairs: integer("total_pairs").notNull(),
|
|
490
493
|
citedPairs: integer("cited_pairs").notNull(),
|
|
491
|
-
providerBreakdown: text("provider_breakdown").notNull().default(
|
|
494
|
+
providerBreakdown: text("provider_breakdown", { mode: "json" }).$type().notNull().default({}),
|
|
492
495
|
createdAt: text("created_at").notNull()
|
|
493
496
|
}, (table) => [
|
|
494
497
|
index("idx_health_snapshots_project").on(table.projectId),
|
|
@@ -588,9 +591,9 @@ var trafficSources = sqliteTable("traffic_sources", {
|
|
|
588
591
|
// observed in the most recent successful sync. Bounded ring buffer used to
|
|
589
592
|
// dedupe across sync runs at the boundary timestamp where lastSyncedAt
|
|
590
593
|
// clamping alone leaves a small overlap window.
|
|
591
|
-
lastEventIds: text("last_event_ids"),
|
|
594
|
+
lastEventIds: text("last_event_ids", { mode: "json" }).$type(),
|
|
592
595
|
archivedAt: text("archived_at"),
|
|
593
|
-
configJson: text("config_json").notNull().default(
|
|
596
|
+
configJson: text("config_json", { mode: "json" }).$type().notNull().default({}),
|
|
594
597
|
createdAt: text("created_at").notNull(),
|
|
595
598
|
updatedAt: text("updated_at").notNull()
|
|
596
599
|
}, (table) => [
|
|
@@ -666,7 +669,7 @@ var rawEventSamples = sqliteTable("raw_event_samples", {
|
|
|
666
669
|
pathNormalized: text("path_normalized").notNull(),
|
|
667
670
|
status: integer("status"),
|
|
668
671
|
refererHost: text("referer_host"),
|
|
669
|
-
classifierDetailsJson: text("classifier_details_json").notNull().default(
|
|
672
|
+
classifierDetailsJson: text("classifier_details_json", { mode: "json" }).$type().notNull().default({}),
|
|
670
673
|
createdAt: text("created_at").notNull()
|
|
671
674
|
}, (table) => [
|
|
672
675
|
index("idx_raw_event_samples_project_ts").on(table.projectId, table.ts),
|
|
@@ -687,7 +690,7 @@ var discoverySessions = sqliteTable("discovery_sessions", {
|
|
|
687
690
|
citedCount: integer("cited_count"),
|
|
688
691
|
aspirationalCount: integer("aspirational_count"),
|
|
689
692
|
wastedCount: integer("wasted_count"),
|
|
690
|
-
competitorMap: text("competitor_map").notNull().default(
|
|
693
|
+
competitorMap: text("competitor_map", { mode: "json" }).$type().notNull().default([]),
|
|
691
694
|
error: text("error"),
|
|
692
695
|
startedAt: text("started_at"),
|
|
693
696
|
finishedAt: text("finished_at"),
|
|
@@ -703,13 +706,43 @@ var discoveryProbes = sqliteTable("discovery_probes", {
|
|
|
703
706
|
query: text("query").notNull(),
|
|
704
707
|
bucket: text("bucket"),
|
|
705
708
|
citationState: text("citation_state").notNull(),
|
|
706
|
-
citedDomains: text("cited_domains").notNull().default(
|
|
709
|
+
citedDomains: text("cited_domains", { mode: "json" }).$type().notNull().default([]),
|
|
707
710
|
rawResponse: text("raw_response"),
|
|
708
711
|
createdAt: text("created_at").notNull()
|
|
709
712
|
}, (table) => [
|
|
710
713
|
index("idx_discovery_probes_session").on(table.sessionId),
|
|
711
714
|
index("idx_discovery_probes_project").on(table.projectId)
|
|
712
715
|
]);
|
|
716
|
+
var contentTargetDismissals = sqliteTable("content_target_dismissals", {
|
|
717
|
+
id: text("id").primaryKey(),
|
|
718
|
+
projectId: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
|
|
719
|
+
targetRef: text("target_ref").notNull(),
|
|
720
|
+
addressedUrl: text("addressed_url"),
|
|
721
|
+
note: text("note"),
|
|
722
|
+
dismissedAt: text("dismissed_at").notNull()
|
|
723
|
+
}, (table) => [
|
|
724
|
+
uniqueIndex("idx_content_target_dismissals_project_ref").on(table.projectId, table.targetRef),
|
|
725
|
+
index("idx_content_target_dismissals_project").on(table.projectId)
|
|
726
|
+
]);
|
|
727
|
+
var recommendationExplanations = sqliteTable("recommendation_explanations", {
|
|
728
|
+
id: text("id").primaryKey(),
|
|
729
|
+
projectId: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
|
|
730
|
+
targetRef: text("target_ref").notNull(),
|
|
731
|
+
promptVersion: text("prompt_version").notNull(),
|
|
732
|
+
provider: text("provider").notNull(),
|
|
733
|
+
model: text("model").notNull(),
|
|
734
|
+
responseText: text("response_text").notNull(),
|
|
735
|
+
/** Estimated cost in millicents (1/100 of a cent) for audit; 0 if unknown. */
|
|
736
|
+
costMillicents: integer("cost_millicents").notNull().default(0),
|
|
737
|
+
generatedAt: text("generated_at").notNull()
|
|
738
|
+
}, (table) => [
|
|
739
|
+
uniqueIndex("idx_recommendation_explanations_unique").on(
|
|
740
|
+
table.projectId,
|
|
741
|
+
table.targetRef,
|
|
742
|
+
table.promptVersion
|
|
743
|
+
),
|
|
744
|
+
index("idx_recommendation_explanations_project").on(table.projectId)
|
|
745
|
+
]);
|
|
713
746
|
var migrationsTable = sqliteTable("_migrations", {
|
|
714
747
|
version: integer("version").primaryKey(),
|
|
715
748
|
name: text("name").notNull(),
|
|
@@ -1941,6 +1974,55 @@ var MIGRATION_VERSIONS = [
|
|
|
1941
1974
|
`CREATE INDEX IF NOT EXISTS idx_audit_log_project ON audit_log(project_id)`,
|
|
1942
1975
|
`CREATE INDEX IF NOT EXISTS idx_audit_log_created ON audit_log(created_at)`
|
|
1943
1976
|
]
|
|
1977
|
+
},
|
|
1978
|
+
{
|
|
1979
|
+
version: 61,
|
|
1980
|
+
name: "content-target-dismissals",
|
|
1981
|
+
// Persistent per-recommendation dismissal so users can mark a content
|
|
1982
|
+
// opportunity "addressed" after they ship the page. The orchestrator
|
|
1983
|
+
// recomputes opportunities on every report load from live GSC / GA
|
|
1984
|
+
// inventory; without persistent dismissal, a recommendation lingers
|
|
1985
|
+
// until the next sync surfaces the new page (days–weeks of lag).
|
|
1986
|
+
//
|
|
1987
|
+
// Keyed by `(project_id, target_ref)` where `target_ref` is the stable
|
|
1988
|
+
// hash that `computeTargetRef()` already produces — same value the
|
|
1989
|
+
// ContentTargetRowDto exposes, so the client passes back the ref it
|
|
1990
|
+
// sees.
|
|
1991
|
+
statements: [
|
|
1992
|
+
`CREATE TABLE IF NOT EXISTS content_target_dismissals (
|
|
1993
|
+
id TEXT PRIMARY KEY,
|
|
1994
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
1995
|
+
target_ref TEXT NOT NULL,
|
|
1996
|
+
addressed_url TEXT,
|
|
1997
|
+
note TEXT,
|
|
1998
|
+
dismissed_at TEXT NOT NULL
|
|
1999
|
+
)`,
|
|
2000
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS idx_content_target_dismissals_project_ref ON content_target_dismissals(project_id, target_ref)`,
|
|
2001
|
+
`CREATE INDEX IF NOT EXISTS idx_content_target_dismissals_project ON content_target_dismissals(project_id)`
|
|
2002
|
+
]
|
|
2003
|
+
},
|
|
2004
|
+
{
|
|
2005
|
+
version: 62,
|
|
2006
|
+
name: "recommendation-explanations",
|
|
2007
|
+
// LLM-generated rationale for content recommendations. Cached per
|
|
2008
|
+
// (project, target_ref, prompt_version) so repeat clicks are free.
|
|
2009
|
+
// Bumping the prompt version invalidates the cache forward without
|
|
2010
|
+
// touching the table.
|
|
2011
|
+
statements: [
|
|
2012
|
+
`CREATE TABLE IF NOT EXISTS recommendation_explanations (
|
|
2013
|
+
id TEXT PRIMARY KEY,
|
|
2014
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
2015
|
+
target_ref TEXT NOT NULL,
|
|
2016
|
+
prompt_version TEXT NOT NULL,
|
|
2017
|
+
provider TEXT NOT NULL,
|
|
2018
|
+
model TEXT NOT NULL,
|
|
2019
|
+
response_text TEXT NOT NULL,
|
|
2020
|
+
cost_millicents INTEGER NOT NULL DEFAULT 0,
|
|
2021
|
+
generated_at TEXT NOT NULL
|
|
2022
|
+
)`,
|
|
2023
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS idx_recommendation_explanations_unique ON recommendation_explanations(project_id, target_ref, prompt_version)`,
|
|
2024
|
+
`CREATE INDEX IF NOT EXISTS idx_recommendation_explanations_project ON recommendation_explanations(project_id)`
|
|
2025
|
+
]
|
|
1944
2026
|
}
|
|
1945
2027
|
];
|
|
1946
2028
|
function isDuplicateColumnError(err) {
|
|
@@ -2635,9 +2717,15 @@ function isBlogShaped(path) {
|
|
|
2635
2717
|
// ../intelligence/src/content-classifier.ts
|
|
2636
2718
|
var SEO_STRONG_THRESHOLD = 10;
|
|
2637
2719
|
var SEO_WEAK_THRESHOLD = 30;
|
|
2720
|
+
function isHomepageOnly(url) {
|
|
2721
|
+
if (url === "/" || url === "") return true;
|
|
2722
|
+
const stripped = url.split("?")[0].replace(/\/+$/, "");
|
|
2723
|
+
return stripped === "" || stripped === "/";
|
|
2724
|
+
}
|
|
2638
2725
|
function classifyContentAction(input) {
|
|
2639
2726
|
const { ourPage, ourPageInGroundingSources, ourPageHasSchema } = input;
|
|
2640
2727
|
if (!ourPage) return "create";
|
|
2728
|
+
if (isHomepageOnly(ourPage.url)) return "create";
|
|
2641
2729
|
if (ourPageInGroundingSources) {
|
|
2642
2730
|
if (ourPageHasSchema === false) return "add-schema";
|
|
2643
2731
|
return null;
|
|
@@ -2782,8 +2870,7 @@ function buildContentTargetRows(input) {
|
|
|
2782
2870
|
const targetRef = computeTargetRef({
|
|
2783
2871
|
projectId: input.projectId,
|
|
2784
2872
|
query: cq.query,
|
|
2785
|
-
action
|
|
2786
|
-
targetPage: ourPage?.url ?? null
|
|
2873
|
+
action
|
|
2787
2874
|
});
|
|
2788
2875
|
const winningCompetitor = pickTopCompetitor(cq.competitorGroundingUrls);
|
|
2789
2876
|
const ourBestPage = ourPage ? {
|
|
@@ -2935,7 +3022,7 @@ function pickTopCompetitor(competitors2) {
|
|
|
2935
3022
|
};
|
|
2936
3023
|
}
|
|
2937
3024
|
function computeTargetRef(input) {
|
|
2938
|
-
const key = [input.projectId, input.query, input.action
|
|
3025
|
+
const key = [input.projectId, input.query, input.action].join("|");
|
|
2939
3026
|
let hash = 0;
|
|
2940
3027
|
for (let i = 0; i < key.length; i++) {
|
|
2941
3028
|
hash = (hash << 5) - hash + key.charCodeAt(i) | 0;
|
|
@@ -3875,7 +3962,12 @@ var IntelligenceService = class {
|
|
|
3875
3962
|
const recentRuns = this.db.select().from(runs).where(
|
|
3876
3963
|
and(
|
|
3877
3964
|
eq(runs.projectId, projectId),
|
|
3878
|
-
or(eq(runs.status, "completed"), eq(runs.status, "partial"))
|
|
3965
|
+
or(eq(runs.status, "completed"), eq(runs.status, "partial")),
|
|
3966
|
+
// Defensive: RunCoordinator already skips probes before this is
|
|
3967
|
+
// called, but if a future call site invokes analyzeAndPersist
|
|
3968
|
+
// directly for a probe, probes still must not pollute the
|
|
3969
|
+
// intelligence window.
|
|
3970
|
+
ne(runs.trigger, RunTriggers.probe)
|
|
3879
3971
|
)
|
|
3880
3972
|
).orderBy(desc(runs.finishedAt), desc(runs.createdAt)).limit(HISTORY_WINDOW_RUNS).all();
|
|
3881
3973
|
if (recentRuns.length === 0) {
|
|
@@ -4009,7 +4101,9 @@ var IntelligenceService = class {
|
|
|
4009
4101
|
const allRuns = this.db.select().from(runs).where(
|
|
4010
4102
|
and(
|
|
4011
4103
|
eq(runs.projectId, project.id),
|
|
4012
|
-
or(eq(runs.status, "completed"), eq(runs.status, "partial"))
|
|
4104
|
+
or(eq(runs.status, "completed"), eq(runs.status, "partial")),
|
|
4105
|
+
// Backfill must not replay probe runs as if they were real sweeps.
|
|
4106
|
+
ne(runs.trigger, RunTriggers.probe)
|
|
4013
4107
|
)
|
|
4014
4108
|
).orderBy(asc(runs.finishedAt)).all();
|
|
4015
4109
|
let startIdx = 0;
|
|
@@ -4128,8 +4222,8 @@ var IntelligenceService = class {
|
|
|
4128
4222
|
title: insight.title,
|
|
4129
4223
|
query: insight.query,
|
|
4130
4224
|
provider: insight.provider,
|
|
4131
|
-
recommendation: insight.recommendation
|
|
4132
|
-
cause: insight.cause
|
|
4225
|
+
recommendation: insight.recommendation ?? null,
|
|
4226
|
+
cause: insight.cause ?? null,
|
|
4133
4227
|
dismissed: wasDismissed,
|
|
4134
4228
|
createdAt: insight.createdAt
|
|
4135
4229
|
}).run();
|
|
@@ -4141,7 +4235,7 @@ var IntelligenceService = class {
|
|
|
4141
4235
|
overallCitedRate: String(result.health.overallCitedRate),
|
|
4142
4236
|
totalPairs: result.health.totalPairs,
|
|
4143
4237
|
citedPairs: result.health.citedPairs,
|
|
4144
|
-
providerBreakdown:
|
|
4238
|
+
providerBreakdown: result.health.providerBreakdown,
|
|
4145
4239
|
createdAt: now
|
|
4146
4240
|
}).run();
|
|
4147
4241
|
});
|
|
@@ -4176,14 +4270,16 @@ var IntelligenceService = class {
|
|
|
4176
4270
|
const projectRow = this.db.select({ locations: projects.locations }).from(projects).where(eq(projects.id, projectId)).get();
|
|
4177
4271
|
const locationCount = Math.max(
|
|
4178
4272
|
1,
|
|
4179
|
-
|
|
4273
|
+
(projectRow?.locations ?? []).length
|
|
4180
4274
|
);
|
|
4181
4275
|
const ROWS_PER_GROUP_BUDGET = Math.max(2, locationCount);
|
|
4182
4276
|
const recentRunRows = this.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(
|
|
4183
4277
|
and(
|
|
4184
4278
|
eq(runs.projectId, projectId),
|
|
4185
4279
|
eq(runs.kind, RunKinds["answer-visibility"]),
|
|
4186
|
-
or(eq(runs.status, "completed"), eq(runs.status, "partial"))
|
|
4280
|
+
or(eq(runs.status, "completed"), eq(runs.status, "partial")),
|
|
4281
|
+
// Defensive — see top of file.
|
|
4282
|
+
ne(runs.trigger, RunTriggers.probe)
|
|
4187
4283
|
)
|
|
4188
4284
|
).orderBy(desc(runs.createdAt), desc(runs.id)).limit((RECURRENCE_LOOKBACK_RUNS + 1) * ROWS_PER_GROUP_BUDGET).all();
|
|
4189
4285
|
const recentGroups = groupRunsByCreatedAt(recentRunRows);
|
|
@@ -4250,8 +4346,8 @@ var IntelligenceService = class {
|
|
|
4250
4346
|
orphanCount++;
|
|
4251
4347
|
continue;
|
|
4252
4348
|
}
|
|
4253
|
-
const domains =
|
|
4254
|
-
const competitors2 =
|
|
4349
|
+
const domains = r.citedDomains;
|
|
4350
|
+
const competitors2 = r.competitorOverlap;
|
|
4255
4351
|
snapshots.push({
|
|
4256
4352
|
query: resolvedQuery,
|
|
4257
4353
|
provider: r.provider,
|
|
@@ -4311,6 +4407,8 @@ export {
|
|
|
4311
4407
|
rawEventSamples,
|
|
4312
4408
|
discoverySessions,
|
|
4313
4409
|
discoveryProbes,
|
|
4410
|
+
contentTargetDismissals,
|
|
4411
|
+
recommendationExplanations,
|
|
4314
4412
|
createClient,
|
|
4315
4413
|
parseJsonColumn,
|
|
4316
4414
|
extractLegacyCredentials,
|