@agentsean/surfaces 1.0.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.
@@ -0,0 +1,56 @@
1
+ /** Bing Webmaster AI Performance has no API. Ingest a CSV export. */
2
+ export function parseBingAiCsv(csv) {
3
+ const lines = csv.trim().split(/\r?\n/).filter(Boolean);
4
+ if (lines.length < 2)
5
+ return [];
6
+ const header = (lines[0] ?? "").toLowerCase();
7
+ const out = [];
8
+ for (const line of lines.slice(1)) {
9
+ const cols = splitCsv(line);
10
+ const date = cols[0] ?? "";
11
+ const groundingQuery = cols[1] ?? "";
12
+ const citations = Number(cols[2] ?? 0);
13
+ const citationShare = Number(cols[3] ?? 0);
14
+ if (!groundingQuery)
15
+ continue;
16
+ out.push({
17
+ date,
18
+ groundingQuery,
19
+ citations: Number.isFinite(citations) ? citations : 0,
20
+ citationShare: Number.isFinite(citationShare) ? citationShare : 0,
21
+ });
22
+ }
23
+ void header;
24
+ return out;
25
+ }
26
+ function splitCsv(line) {
27
+ const out = [];
28
+ let cur = "";
29
+ let q = false;
30
+ for (const ch of line) {
31
+ if (ch === '"') {
32
+ q = !q;
33
+ continue;
34
+ }
35
+ if (ch === "," && !q) {
36
+ out.push(cur.trim());
37
+ cur = "";
38
+ continue;
39
+ }
40
+ cur += ch;
41
+ }
42
+ out.push(cur.trim());
43
+ return out;
44
+ }
45
+ export function bingCitationShare(rows) {
46
+ if (rows.length === 0)
47
+ return 0;
48
+ let w = 0;
49
+ let n = 0;
50
+ for (const r of rows) {
51
+ w += r.citationShare * Math.max(r.citations, 1);
52
+ n += Math.max(r.citations, 1);
53
+ }
54
+ return n === 0 ? 0 : w / n;
55
+ }
56
+ //# sourceMappingURL=bing-ai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bing-ai.js","sourceRoot":"","sources":["../src/bing-ai.ts"],"names":[],"mappings":"AAAA,qEAAqE;AASrE,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACxD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAChC,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9C,MAAM,GAAG,GAAgB,EAAE,CAAC;IAC5B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3B,MAAM,cAAc,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACrC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACvC,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3C,IAAI,CAAC,cAAc;YAAE,SAAS;QAC9B,GAAG,CAAC,IAAI,CAAC;YACP,IAAI;YACJ,cAAc;YACd,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YACrD,aAAa,EAAE,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;SAClE,CAAC,CAAC;IACL,CAAC;IACD,KAAK,MAAM,CAAC;IACZ,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,IAAI,CAAC,GAAG,KAAK,CAAC;IACd,KAAK,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC;QACtB,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,CAAC,GAAG,CAAC,CAAC,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC;YACrB,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YACrB,GAAG,GAAG,EAAE,CAAC;YACT,SAAS;QACX,CAAC;QACD,GAAG,IAAI,EAAE,CAAC;IACZ,CAAC;IACD,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IACrB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAiB;IACjD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAChC,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,CAAC,IAAI,CAAC,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QAChD,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { type SqliteDatabase } from "@agentsean/db";
2
+ import type { GenerateFn } from "@agentsean/llm";
3
+ import { detectVertical, type VerticalInput, type VerticalPreset } from "./verticals.js";
4
+ export type SurfacesJobResult = {
5
+ citationShare: number;
6
+ shareOfVoice: number;
7
+ estimatedUsd: number;
8
+ bingShare: number | null;
9
+ vertical: VerticalPreset;
10
+ verticalConfidence: number;
11
+ suppressedChecks: number;
12
+ mentions: number;
13
+ inbound404s: number;
14
+ gbpLocations: number;
15
+ aiCitationGap: boolean;
16
+ generationBlocked: boolean;
17
+ rules: string[];
18
+ };
19
+ export declare function runVerticalDetect(db: SqliteDatabase, siteId: string, extra?: Partial<VerticalInput>): ReturnType<typeof detectVertical>;
20
+ export declare function runSurfacesJob(db: SqliteDatabase, opts: {
21
+ siteId: string;
22
+ origin: string;
23
+ brand?: string | undefined;
24
+ generate?: GenerateFn | undefined;
25
+ bingCsv?: string | undefined;
26
+ mentionPages?: Array<{
27
+ url: string;
28
+ text: string;
29
+ links: string[];
30
+ }> | undefined;
31
+ crawl404s?: Array<{
32
+ url: string;
33
+ statusCode: number | null;
34
+ inlinks: string[];
35
+ }> | undefined;
36
+ now?: Date | undefined;
37
+ }): Promise<SurfacesJobResult>;
38
+ export declare function saveOnboardingAnswers(db: SqliteDatabase, siteId: string, answers: Record<string, string>): ReturnType<typeof detectVertical>;
39
+ export declare function t4CityServiceIsRefused(): boolean;
40
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAYjD,OAAO,EAEL,cAAc,EAGd,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,gBAAgB,CAAC;AAExB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,cAAc,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAEF,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,cAAc,EAClB,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAC7B,UAAU,CAAC,OAAO,cAAc,CAAC,CA2DnC;AAED,wBAAsB,cAAc,CAClC,EAAE,EAAE,cAAc,EAClB,IAAI,EAAE;IACJ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,GAAG,SAAS,CAAC;IACjF,SAAS,CAAC,EACR,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,GAAG,SAAS,CAAC;IACnF,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;CACxB,GACA,OAAO,CAAC,iBAAiB,CAAC,CAoG5B;AAED,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,cAAc,EAClB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,UAAU,CAAC,OAAO,cAAc,CAAC,CAiBnC;AAED,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD"}
package/dist/engine.js ADDED
@@ -0,0 +1,187 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { eq } from "drizzle-orm";
3
+ import { aiCitations, aiRuns, bingAiRows, pages, sites, verticalProfiles, } from "@agentsean/db";
4
+ import { KIND_TIER } from "@agentsean/actions";
5
+ import { bingCitationShare, parseBingAiCsv } from "./bing-ai.js";
6
+ import { PANEL_COST_USD } from "./honest.js";
7
+ import { listGbpLocations, localCitationGap } from "./gbp.js";
8
+ import { discoverMentions, findInbound404s, listMentions, saveInbound404s, saveMentions, } from "./offpage.js";
9
+ import { runPromptPanel } from "./panel.js";
10
+ import { contentGenerationBlocked, detectVertical, verticalRules, ymylCategoryFor, } from "./verticals.js";
11
+ export function runVerticalDetect(db, siteId, extra) {
12
+ const pageRows = db.select().from(pages).where(eq(pages.siteId, siteId)).all();
13
+ const urls = pageRows.map((p) => p.url);
14
+ const titles = pageRows.map((p) => p.title ?? "");
15
+ const input = {
16
+ urls,
17
+ titles,
18
+ jsonLdTypes: extra?.jsonLdTypes ?? [],
19
+ outboundSponsoredShare: extra?.outboundSponsoredShare ?? 0,
20
+ telLinkShare: extra?.telLinkShare ?? 0,
21
+ gscNewsRows: extra?.gscNewsRows ?? 0,
22
+ gscDiscoverShare: extra?.gscDiscoverShare ?? 0,
23
+ newsSitemap: extra?.newsSitemap ?? false,
24
+ publishVelocity30d: extra?.publishVelocity30d ?? 0,
25
+ jobPostingCount: extra?.jobPostingCount ?? 0,
26
+ sitemapUrlCount: extra?.sitemapUrlCount ?? urls.length,
27
+ productSellerVaries: extra?.productSellerVaries ?? false,
28
+ profileUrlCount: extra?.profileUrlCount ?? 0,
29
+ localityCount: extra?.localityCount ?? 0,
30
+ eeaTrafficShare: extra?.eeaTrafficShare ?? 0,
31
+ hasPricing: urls.some((u) => /\/pricing\b/i.test(u)),
32
+ hasDemoOrSignup: urls.some((u) => /\/(demo|signup|free-trial)\b/i.test(u)),
33
+ hasComparePaths: urls.some((u) => /\/(vs|alternatives|compare|integrations)\b/i.test(u)),
34
+ medicalLexiconShare: extra?.medicalLexiconShare ?? 0,
35
+ };
36
+ const detected = detectVertical(input);
37
+ const existing = db
38
+ .select()
39
+ .from(verticalProfiles)
40
+ .where(eq(verticalProfiles.siteId, siteId))
41
+ .get();
42
+ const values = {
43
+ preset: detected.preset,
44
+ confidence: detected.confidence,
45
+ signalsJson: JSON.stringify(detected.signals),
46
+ answersJson: existing?.answersJson ?? "{}",
47
+ suppressedChecks: detected.suppressedChecks,
48
+ updatedAt: new Date().toISOString(),
49
+ };
50
+ if (existing) {
51
+ db.update(verticalProfiles)
52
+ .set(values)
53
+ .where(eq(verticalProfiles.id, existing.id))
54
+ .run();
55
+ }
56
+ else {
57
+ db.insert(verticalProfiles)
58
+ .values({ id: randomUUID(), siteId, ...values })
59
+ .run();
60
+ }
61
+ const cat = ymylCategoryFor(detected.preset);
62
+ if (cat) {
63
+ db.update(sites)
64
+ .set({ ymylCategory: cat, updatedAt: new Date().toISOString() })
65
+ .where(eq(sites.id, siteId))
66
+ .run();
67
+ }
68
+ return detected;
69
+ }
70
+ export async function runSurfacesJob(db, opts) {
71
+ const now = opts.now ?? new Date();
72
+ const brand = opts.brand ??
73
+ new URL(opts.origin).hostname.replace(/^www\./, "").split(".")[0] ??
74
+ "brand";
75
+ const detected = runVerticalDetect(db, opts.siteId);
76
+ let citationShare = 0;
77
+ let shareOfVoice = 0;
78
+ let estimatedUsd = 0;
79
+ if (opts.generate) {
80
+ const panel = await runPromptPanel({
81
+ origin: opts.origin,
82
+ brand,
83
+ generate: opts.generate,
84
+ });
85
+ citationShare = panel.citationShare;
86
+ shareOfVoice = panel.shareOfVoice;
87
+ estimatedUsd = panel.estimatedUsd;
88
+ const runId = randomUUID();
89
+ db.insert(aiRuns)
90
+ .values({
91
+ id: runId,
92
+ siteId: opts.siteId,
93
+ engine: "panel",
94
+ promptCount: panel.hits.length,
95
+ citationShare,
96
+ shareOfVoice,
97
+ estimatedUsd,
98
+ ranAt: now.toISOString(),
99
+ })
100
+ .run();
101
+ for (const hit of panel.hits) {
102
+ db.insert(aiCitations)
103
+ .values({
104
+ id: randomUUID(),
105
+ runId,
106
+ engine: hit.engine,
107
+ prompt: hit.prompt,
108
+ citedUrl: hit.citedUrl,
109
+ citedDomain: hit.citedDomain,
110
+ isOurs: hit.isOurs ? 1 : 0,
111
+ })
112
+ .run();
113
+ }
114
+ }
115
+ let bingShare = null;
116
+ if (opts.bingCsv) {
117
+ const rows = parseBingAiCsv(opts.bingCsv);
118
+ bingShare = bingCitationShare(rows);
119
+ for (const row of rows) {
120
+ db.insert(bingAiRows)
121
+ .values({
122
+ id: randomUUID(),
123
+ siteId: opts.siteId,
124
+ date: row.date,
125
+ groundingQuery: row.groundingQuery,
126
+ citations: row.citations,
127
+ citationShare: row.citationShare,
128
+ source: "csv",
129
+ })
130
+ .run();
131
+ }
132
+ if (!opts.generate)
133
+ citationShare = bingShare;
134
+ }
135
+ const mentionPages = opts.mentionPages ?? [];
136
+ const found = discoverMentions({
137
+ brand,
138
+ originHost: new URL(opts.origin).hostname,
139
+ pages: mentionPages,
140
+ });
141
+ saveMentions(db, opts.siteId, found, now);
142
+ const broken = findInbound404s(opts.crawl404s ?? []);
143
+ const inboundN = saveInbound404s(db, opts.siteId, broken, now);
144
+ const gbps = listGbpLocations(db, opts.siteId);
145
+ const gap = localCitationGap({
146
+ gbpListed: gbps.length > 0,
147
+ aiMentions: listMentions(db, opts.siteId).length,
148
+ localPackVisible: gbps.length > 0,
149
+ });
150
+ return {
151
+ citationShare,
152
+ shareOfVoice,
153
+ estimatedUsd: estimatedUsd || (opts.generate ? PANEL_COST_USD : 0),
154
+ bingShare,
155
+ vertical: detected.preset,
156
+ verticalConfidence: detected.confidence,
157
+ suppressedChecks: detected.suppressedChecks,
158
+ mentions: listMentions(db, opts.siteId).length,
159
+ inbound404s: inboundN,
160
+ gbpLocations: gbps.length,
161
+ aiCitationGap: gap.gap,
162
+ generationBlocked: contentGenerationBlocked(detected.preset),
163
+ rules: verticalRules(detected.preset),
164
+ };
165
+ }
166
+ export function saveOnboardingAnswers(db, siteId, answers) {
167
+ const detected = runVerticalDetect(db, siteId);
168
+ const existing = db
169
+ .select()
170
+ .from(verticalProfiles)
171
+ .where(eq(verticalProfiles.siteId, siteId))
172
+ .get();
173
+ if (existing) {
174
+ db.update(verticalProfiles)
175
+ .set({
176
+ answersJson: JSON.stringify(answers),
177
+ updatedAt: new Date().toISOString(),
178
+ })
179
+ .where(eq(verticalProfiles.id, existing.id))
180
+ .run();
181
+ }
182
+ return detected;
183
+ }
184
+ export function t4CityServiceIsRefused() {
185
+ return KIND_TIER.create_city_service_page === 4;
186
+ }
187
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
package/dist/gbp.d.ts ADDED
@@ -0,0 +1,51 @@
1
+ import { type SqliteDatabase } from "@agentsean/db";
2
+ /** 10 edits/min per profile, non-increasable. 300 QPM per API. Quota starts at 0 until approval. */
3
+ export declare const GBP_EDITS_PER_MIN = 10;
4
+ export declare const GBP_QPM = 300;
5
+ export declare class GbpQuotaError extends Error {
6
+ readonly name = "GbpQuotaError";
7
+ }
8
+ export declare class GbpNotApprovedError extends Error {
9
+ readonly name = "GbpNotApprovedError";
10
+ }
11
+ export type GbpLocation = {
12
+ id: string;
13
+ locationName: string;
14
+ title: string | null;
15
+ primaryCategory: string | null;
16
+ approvalStatus: string;
17
+ };
18
+ export type GbpWrite = {
19
+ locationId: string;
20
+ kind: "hours" | "category" | "title";
21
+ payload: Record<string, unknown>;
22
+ };
23
+ export declare function allowGbpEdit(locationId: string, now?: number): boolean;
24
+ export declare function refuseReviewGeneration(): never;
25
+ export declare function refuseCityServicePages(): never;
26
+ export declare function refuseGbpTitleWrite(): never;
27
+ export declare function applyGbpEdit(db: SqliteDatabase, siteId: string, write: GbpWrite, now?: Date): Promise<{
28
+ id: string;
29
+ }>;
30
+ export declare function upsertGbpLocation(db: SqliteDatabase, siteId: string, loc: Omit<GbpLocation, "id"> & {
31
+ id?: string | undefined;
32
+ }): string;
33
+ export declare function listGbpLocations(db: SqliteDatabase, siteId: string): {
34
+ id: string;
35
+ siteId: string;
36
+ locationName: string;
37
+ title: string | null;
38
+ primaryCategory: string | null;
39
+ placeId: string | null;
40
+ approvalStatus: string;
41
+ verifiedAt: string | null;
42
+ }[];
43
+ export declare function localCitationGap(opts: {
44
+ gbpListed: boolean;
45
+ aiMentions: number;
46
+ localPackVisible: boolean;
47
+ }): {
48
+ gap: boolean;
49
+ message: string;
50
+ };
51
+ //# sourceMappingURL=gbp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gbp.d.ts","sourceRoot":"","sources":["../src/gbp.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0B,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAE5E,oGAAoG;AACpG,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,eAAO,MAAM,OAAO,MAAM,CAAC;AAE3B,qBAAa,aAAc,SAAQ,KAAK;IACtC,SAAkB,IAAI,mBAAmB;CAC1C;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,SAAkB,IAAI,yBAAyB;CAChD;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,OAAO,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC,CAAC;AAIF,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,OAAO,CAO1E;AAED,wBAAgB,sBAAsB,IAAI,KAAK,CAI9C;AAED,wBAAgB,sBAAsB,IAAI,KAAK,CAI9C;AAED,wBAAgB,mBAAmB,IAAI,KAAK,CAI3C;AAED,wBAAsB,YAAY,CAChC,EAAE,EAAE,cAAc,EAClB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,QAAQ,EACf,GAAG,OAAa,GACf,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CA0DzB;AAED,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,cAAc,EAClB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACzD,MAAM,CAmBR;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM;;;;;;;;;IAElE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;CAC3B,GAAG;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CASpC"}
package/dist/gbp.js ADDED
@@ -0,0 +1,114 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { eq } from "drizzle-orm";
3
+ import { gbpEdits, gbpLocations } from "@agentsean/db";
4
+ /** 10 edits/min per profile, non-increasable. 300 QPM per API. Quota starts at 0 until approval. */
5
+ export const GBP_EDITS_PER_MIN = 10;
6
+ export const GBP_QPM = 300;
7
+ export class GbpQuotaError extends Error {
8
+ name = "GbpQuotaError";
9
+ }
10
+ export class GbpNotApprovedError extends Error {
11
+ name = "GbpNotApprovedError";
12
+ }
13
+ const buckets = new Map();
14
+ export function allowGbpEdit(locationId, now = Date.now()) {
15
+ const windowStart = now - 60_000;
16
+ const hits = (buckets.get(locationId) ?? []).filter((t) => t > windowStart);
17
+ if (hits.length >= GBP_EDITS_PER_MIN)
18
+ return false;
19
+ hits.push(now);
20
+ buckets.set(locationId, hits);
21
+ return true;
22
+ }
23
+ export function refuseReviewGeneration() {
24
+ throw new Error("Review generation is T4. Incentives, gating, and staff-name asks are explicit GBP policy violations. No setting exists.");
25
+ }
26
+ export function refuseCityServicePages() {
27
+ throw new Error("Unbounded city×service page generation is T4. It is named under both doorway abuse and scaled content abuse.");
28
+ }
29
+ export function refuseGbpTitleWrite() {
30
+ throw new Error("Never auto-write the GBP business title. Keywords in the title are a policy trap. Advisory only.");
31
+ }
32
+ export async function applyGbpEdit(db, siteId, write, now = new Date()) {
33
+ const loc = db
34
+ .select()
35
+ .from(gbpLocations)
36
+ .where(eq(gbpLocations.id, write.locationId))
37
+ .get();
38
+ if (!loc || loc.approvalStatus !== "approved") {
39
+ throw new GbpNotApprovedError("GBP write APIs start at 0 QPM until Google approves Basic API Access and the profile is verified 60+ days.");
40
+ }
41
+ if (write.kind === "title")
42
+ refuseGbpTitleWrite();
43
+ const windowStart = new Date(now.getTime() - 60_000).toISOString();
44
+ const recentForProfile = db
45
+ .select()
46
+ .from(gbpEdits)
47
+ .where(eq(gbpEdits.locationId, write.locationId))
48
+ .all()
49
+ .filter((e) => e.appliedAt >= windowStart);
50
+ if (recentForProfile.length >= GBP_EDITS_PER_MIN ||
51
+ !allowGbpEdit(write.locationId, now.getTime())) {
52
+ throw new GbpQuotaError(`GBP cap is ${GBP_EDITS_PER_MIN} edits/min per profile and is not increasable.`);
53
+ }
54
+ const recentAll = db
55
+ .select()
56
+ .from(gbpEdits)
57
+ .all()
58
+ .filter((e) => e.appliedAt >= windowStart);
59
+ if (recentAll.length >= GBP_QPM) {
60
+ throw new GbpQuotaError(`GBP API cap is ${GBP_QPM} QPM and starts at 0 until Google approves Basic API Access.`);
61
+ }
62
+ if (write.kind === "category" &&
63
+ typeof write.payload["primaryCategory"] === "string") {
64
+ db.update(gbpLocations)
65
+ .set({ primaryCategory: write.payload["primaryCategory"] })
66
+ .where(eq(gbpLocations.id, write.locationId))
67
+ .run();
68
+ }
69
+ const id = randomUUID();
70
+ db.insert(gbpEdits)
71
+ .values({
72
+ id,
73
+ siteId,
74
+ locationId: write.locationId,
75
+ kind: write.kind,
76
+ payload: JSON.stringify(write.payload),
77
+ appliedAt: now.toISOString(),
78
+ })
79
+ .run();
80
+ return { id };
81
+ }
82
+ export function upsertGbpLocation(db, siteId, loc) {
83
+ const id = loc.id ?? randomUUID();
84
+ const existing = db.select().from(gbpLocations).where(eq(gbpLocations.id, id)).get();
85
+ const values = {
86
+ siteId,
87
+ locationName: loc.locationName,
88
+ title: loc.title,
89
+ primaryCategory: loc.primaryCategory,
90
+ approvalStatus: loc.approvalStatus,
91
+ verifiedAt: loc.approvalStatus === "approved" ? new Date().toISOString() : null,
92
+ };
93
+ if (existing) {
94
+ db.update(gbpLocations).set(values).where(eq(gbpLocations.id, id)).run();
95
+ return id;
96
+ }
97
+ db.insert(gbpLocations)
98
+ .values({ id, placeId: null, ...values })
99
+ .run();
100
+ return id;
101
+ }
102
+ export function listGbpLocations(db, siteId) {
103
+ return db.select().from(gbpLocations).where(eq(gbpLocations.siteId, siteId)).all();
104
+ }
105
+ export function localCitationGap(opts) {
106
+ if (opts.gbpListed && opts.localPackVisible && opts.aiMentions === 0) {
107
+ return {
108
+ gap: true,
109
+ message: "BrightLocal 2026: AI tools jumped from 6% to 45% of local discovery while Google review-reading fell 83% → 71%. An AI citation gap is now worth more than another local rank tracker.",
110
+ };
111
+ }
112
+ return { gap: false, message: "No AI citation gap flagged." };
113
+ }
114
+ //# sourceMappingURL=gbp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gbp.js","sourceRoot":"","sources":["../src/gbp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /** Claims we refuse to sell as AEO levers. PLAN Phase 9 / Ahrefs DiD / arXiv:2604.25707. */
2
+ export declare const AEO_REFUSALS: readonly [{
3
+ readonly claim: "Add schema markup to win AI citations";
4
+ readonly truth: "Ahrefs matched difference-in-differences (1,885 pages): schema has no measurable effect on AI citations.";
5
+ }, {
6
+ readonly claim: "Longer pages get cited more";
7
+ readonly truth: "Content length is uncorrelated with AI citations (r = 0.04).";
8
+ }, {
9
+ readonly claim: "Publish llms.txt for GEO";
10
+ readonly truth: "97% of published llms.txt files are never fetched (Ahrefs, 137,210 domains). Google does not support the file.";
11
+ }];
12
+ export declare const EVIDENCE_SPEC: readonly ["definitions", "numeric_facts", "comparisons", "procedures"];
13
+ export declare const PANEL_COST_USD = 1.11;
14
+ export declare const PANEL_PROMPTS = 20;
15
+ export declare const PANEL_ENGINES: readonly ["chatgpt", "gemini"];
16
+ export declare function refuseAeoLever(claim: string): string;
17
+ //# sourceMappingURL=honest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"honest.d.ts","sourceRoot":"","sources":["../src/honest.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAE5F,eAAO,MAAM,YAAY;;;;;;;;;EAef,CAAC;AAEX,eAAO,MAAM,aAAa,wEAKhB,CAAC;AAEX,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,aAAa,gCAAiC,CAAC;AAE5D,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIpD"}
package/dist/honest.js ADDED
@@ -0,0 +1,31 @@
1
+ /** Claims we refuse to sell as AEO levers. PLAN Phase 9 / Ahrefs DiD / arXiv:2604.25707. */
2
+ export const AEO_REFUSALS = [
3
+ {
4
+ claim: "Add schema markup to win AI citations",
5
+ truth: "Ahrefs matched difference-in-differences (1,885 pages): schema has no measurable effect on AI citations.",
6
+ },
7
+ {
8
+ claim: "Longer pages get cited more",
9
+ truth: "Content length is uncorrelated with AI citations (r = 0.04).",
10
+ },
11
+ {
12
+ claim: "Publish llms.txt for GEO",
13
+ truth: "97% of published llms.txt files are never fetched (Ahrefs, 137,210 domains). Google does not support the file.",
14
+ },
15
+ ];
16
+ export const EVIDENCE_SPEC = [
17
+ "definitions",
18
+ "numeric_facts",
19
+ "comparisons",
20
+ "procedures",
21
+ ];
22
+ export const PANEL_COST_USD = 1.11;
23
+ export const PANEL_PROMPTS = 20;
24
+ export const PANEL_ENGINES = ["chatgpt", "gemini"];
25
+ export function refuseAeoLever(claim) {
26
+ const hit = AEO_REFUSALS.find((r) => r.claim.toLowerCase() === claim.toLowerCase());
27
+ if (hit)
28
+ return hit.truth;
29
+ return "Sean does not sell schema, word count, or llms.txt as AEO levers. Citation selection and answer absorption are distinct outcomes; high-impact pages are dense in extractable evidence.";
30
+ }
31
+ //# sourceMappingURL=honest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"honest.js","sourceRoot":"","sources":["../src/honest.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAE5F,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B;QACE,KAAK,EAAE,uCAAuC;QAC9C,KAAK,EACH,0GAA0G;KAC7G;IACD;QACE,KAAK,EAAE,6BAA6B;QACpC,KAAK,EAAE,8DAA8D;KACtE;IACD;QACE,KAAK,EAAE,0BAA0B;QACjC,KAAK,EACH,gHAAgH;KACnH;CACO,CAAC;AAEX,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,aAAa;IACb,eAAe;IACf,aAAa;IACb,YAAY;CACJ,CAAC;AAEX,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AACnC,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,CAAC;AAChC,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAU,CAAC;AAE5D,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;IACpF,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC,KAAK,CAAC;IAC1B,OAAO,wLAAwL,CAAC;AAClM,CAAC"}
@@ -0,0 +1,8 @@
1
+ export { AEO_REFUSALS, EVIDENCE_SPEC, PANEL_COST_USD, PANEL_PROMPTS, PANEL_ENGINES, refuseAeoLever, } from "./honest.js";
2
+ export { defaultPrompts, parseCitations, citationShare, shareOfVoice, runPromptPanel, type CitationHit, type PanelEngine, } from "./panel.js";
3
+ export { parseBingAiCsv, bingCitationShare, type BingAiRow } from "./bing-ai.js";
4
+ export { GBP_EDITS_PER_MIN, GBP_QPM, GbpQuotaError, GbpNotApprovedError, allowGbpEdit, applyGbpEdit, upsertGbpLocation, listGbpLocations, localCitationGap, refuseReviewGeneration, refuseCityServicePages, refuseGbpTitleWrite, } from "./gbp.js";
5
+ export { scoreProspect, discoverMentions, findInbound404s, saveMentions, saveInbound404s, draftOutreach, listMentions, listOutreach, listInbound404s, refuseUnauthedSend, refuseDisavowWithoutManualAction, } from "./offpage.js";
6
+ export { VERTICAL_PRESETS, ONBOARDING_QUESTIONS, detectVertical, scoreSignals, contentGenerationBlocked, ymylCategoryFor, verticalRules, type VerticalPreset, type VerticalInput, type VerticalSignal, } from "./verticals.js";
7
+ export { runSurfacesJob, runVerticalDetect, saveOnboardingAnswers, t4CityServiceIsRefused, type SurfacesJobResult, } from "./engine.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,EACb,aAAa,EACb,cAAc,GACf,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,cAAc,EACd,aAAa,EACb,YAAY,EACZ,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,WAAW,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,EACL,iBAAiB,EACjB,OAAO,EACP,aAAa,EACb,mBAAmB,EACnB,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,sBAAsB,EACtB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,eAAe,EACf,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,gCAAgC,GACjC,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,cAAc,EACd,YAAY,EACZ,wBAAwB,EACxB,eAAe,EACf,aAAa,EACb,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,KAAK,iBAAiB,GACvB,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { AEO_REFUSALS, EVIDENCE_SPEC, PANEL_COST_USD, PANEL_PROMPTS, PANEL_ENGINES, refuseAeoLever, } from "./honest.js";
2
+ export { defaultPrompts, parseCitations, citationShare, shareOfVoice, runPromptPanel, } from "./panel.js";
3
+ export { parseBingAiCsv, bingCitationShare } from "./bing-ai.js";
4
+ export { GBP_EDITS_PER_MIN, GBP_QPM, GbpQuotaError, GbpNotApprovedError, allowGbpEdit, applyGbpEdit, upsertGbpLocation, listGbpLocations, localCitationGap, refuseReviewGeneration, refuseCityServicePages, refuseGbpTitleWrite, } from "./gbp.js";
5
+ export { scoreProspect, discoverMentions, findInbound404s, saveMentions, saveInbound404s, draftOutreach, listMentions, listOutreach, listInbound404s, refuseUnauthedSend, refuseDisavowWithoutManualAction, } from "./offpage.js";
6
+ export { VERTICAL_PRESETS, ONBOARDING_QUESTIONS, detectVertical, scoreSignals, contentGenerationBlocked, ymylCategoryFor, verticalRules, } from "./verticals.js";
7
+ export { runSurfacesJob, runVerticalDetect, saveOnboardingAnswers, t4CityServiceIsRefused, } from "./engine.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,EACb,aAAa,EACb,cAAc,GACf,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,cAAc,EACd,aAAa,EACb,YAAY,EACZ,cAAc,GAGf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAkB,MAAM,cAAc,CAAC;AACjF,OAAO,EACL,iBAAiB,EACjB,OAAO,EACP,aAAa,EACb,mBAAmB,EACnB,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,sBAAsB,EACtB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,eAAe,EACf,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,gCAAgC,GACjC,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,cAAc,EACd,YAAY,EACZ,wBAAwB,EACxB,eAAe,EACf,aAAa,GAId,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,GAEvB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,69 @@
1
+ import { type SqliteDatabase } from "@agentsean/db";
2
+ export type MentionInput = {
3
+ url: string;
4
+ snippet: string;
5
+ linked: boolean;
6
+ kind?: "unlinked" | "inbound_404" | "prospect" | undefined;
7
+ };
8
+ export declare function scoreProspect(opts: {
9
+ linked: boolean;
10
+ domainRating?: number | undefined;
11
+ branded: boolean;
12
+ }): number;
13
+ export declare function discoverMentions(opts: {
14
+ brand: string;
15
+ originHost: string;
16
+ pages: Array<{
17
+ url: string;
18
+ text: string;
19
+ links: string[];
20
+ }>;
21
+ }): MentionInput[];
22
+ export declare function findInbound404s(crawled: Array<{
23
+ url: string;
24
+ statusCode: number | null;
25
+ inlinks: string[];
26
+ }>): Array<{
27
+ sourceUrl: string;
28
+ targetUrl: string;
29
+ status: number | null;
30
+ }>;
31
+ export declare function saveMentions(db: SqliteDatabase, siteId: string, rows: MentionInput[], now?: Date): number;
32
+ export declare function saveInbound404s(db: SqliteDatabase, siteId: string, rows: Array<{
33
+ sourceUrl: string;
34
+ targetUrl: string;
35
+ status: number | null;
36
+ }>, now?: Date): number;
37
+ export declare function draftOutreach(db: SqliteDatabase, siteId: string, mentionId: string, now?: Date): string;
38
+ export declare function refuseUnauthedSend(): never;
39
+ export declare function refuseDisavowWithoutManualAction(): never;
40
+ export declare function listMentions(db: SqliteDatabase, siteId: string): {
41
+ id: string;
42
+ siteId: string;
43
+ url: string;
44
+ snippet: string;
45
+ linked: number;
46
+ score: number;
47
+ kind: string;
48
+ createdAt: string;
49
+ }[];
50
+ export declare function listOutreach(db: SqliteDatabase, siteId: string): {
51
+ id: string;
52
+ siteId: string;
53
+ mentionId: string | null;
54
+ toEmail: string | null;
55
+ subject: string;
56
+ body: string;
57
+ state: string;
58
+ createdAt: string;
59
+ }[];
60
+ export declare function listInbound404s(db: SqliteDatabase, siteId: string): {
61
+ id: string;
62
+ siteId: string;
63
+ sourceUrl: string;
64
+ targetUrl: string;
65
+ status: number | null;
66
+ createdAt: string;
67
+ }[];
68
+ export declare function brandHostFromOrigin(origin: string): string;
69
+ //# sourceMappingURL=offpage.d.ts.map