@canonry/canonry 5.20.0 → 5.20.2
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/README.md +8 -8
- package/assets/agent-workspace/skills/aero/references/aeo-discovery.md +4 -4
- package/assets/agent-workspace/skills/canonry/references/google-business-profile.md +6 -6
- package/dist/{chunk-WL27HO5C.js → chunk-7YGOE4R2.js} +3 -3
- package/dist/{chunk-TSFKNQ5X.js → chunk-CVUR3O5G.js} +3 -3
- package/dist/{chunk-XBGPHZKA.js → chunk-JJPZ4OQ2.js} +1 -1
- package/dist/{chunk-4PENJKRN.js → chunk-KHNPBUUN.js} +2 -2
- package/dist/{chunk-5FKR2K76.js → chunk-PXVJ3P5G.js} +2 -2
- package/dist/{chunk-DRZWMFRV.js → chunk-RV3PYKXP.js} +70 -28
- package/dist/{chunk-QF7SY7AF.js → chunk-TOSPZ7I5.js} +2 -0
- package/dist/cli.js +8 -8
- package/dist/{demo-server-OCBV5OMS.js → demo-server-JYDAUEP2.js} +4 -4
- package/dist/index.js +6 -6
- package/dist/{intelligence-service-MKHINDHN.js → intelligence-service-H4LGPJLZ.js} +2 -2
- package/dist/mcp.js +4 -4
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -166,14 +166,6 @@ For a configured portfolio destination, select one `--market <key>` or `--proper
|
|
|
166
166
|
|
|
167
167
|
<a id="compare-competitors-and-measure-complex-portfolios"></a>
|
|
168
168
|
|
|
169
|
-
### Portfolios and competitors
|
|
170
|
-
|
|
171
|
-
Use [versioned measurement plans](docs/mcp.md#tool-surface) for portfolios of locations, products, or site sections.
|
|
172
|
-
|
|
173
|
-
- **Properties:** Assign queries, engines, models, and locations to each property. Keep branded and non-brand coverage separate.
|
|
174
|
-
- **Market groups:** Organize overlapping sets of properties, each with its own competitors.
|
|
175
|
-
- **Comparisons:** Compare properties, groups, engines, and locations. Open the exact answers and cited URLs behind each result.
|
|
176
|
-
|
|
177
169
|
### Search, traffic, and site health
|
|
178
170
|
|
|
179
171
|
Investigate visibility changes with evidence from search and local performance, crawler visits, AI page fetches, referrals, and conversions.
|
|
@@ -183,6 +175,14 @@ Site audits show technical findings alongside that evidence.
|
|
|
183
175
|
|
|
184
176
|
*Map crawlable pages and the internal links that connect them.*
|
|
185
177
|
|
|
178
|
+
### Portfolios and competitors
|
|
179
|
+
|
|
180
|
+
Use [versioned measurement plans](docs/mcp.md#tool-surface) for portfolios of locations, products, or site sections.
|
|
181
|
+
|
|
182
|
+
- **Properties:** Assign queries, engines, models, and locations to each property. Keep branded and non-brand coverage separate.
|
|
183
|
+
- **Market groups:** Organize overlapping sets of properties, each with its own competitors.
|
|
184
|
+
- **Comparisons:** Compare properties, groups, engines, and locations. Open the exact answers and cited URLs behind each result.
|
|
185
|
+
|
|
186
186
|
## Act on the evidence
|
|
187
187
|
|
|
188
188
|
Your agent coordinates the work through Canonry and its own tools.
|
|
@@ -136,7 +136,7 @@ Keep it tight. The operator wakes to a short, decision-ready summary, not a full
|
|
|
136
136
|
- **Gemini not configured** → orchestrator throws early; `runs.status='failed'` with `Gemini provider is not configured.` Surface as "configure Gemini before running discovery" — link to `cnry init` or `~/.canonry/config.yaml`.
|
|
137
137
|
- **Vertex-only Gemini** → embeddings step throws (Vertex embeddings deferred). Same surface, "use a Gemini API key for now."
|
|
138
138
|
- **ICP missing** → route returns 400 with `VALIDATION_ERROR`. Ask the operator for the ICP description in plain language.
|
|
139
|
-
- **Seed collapse (hyperlocal/niche businesses)** → 40 raw seeds collapse to 1-2 canonical queries after embedding+clustering, even at low dedup thresholds. This happens when Gemini generates seed queries that all live in the same semantic pocket (e.g. all variants of "boutique hotel
|
|
139
|
+
- **Seed collapse (hyperlocal/niche businesses)** → 40 raw seeds collapse to 1-2 canonical queries after embedding+clustering, even at low dedup thresholds. This happens when Gemini generates seed queries that all live in the same semantic pocket (e.g. all variants of "boutique hotel Harborline Beach"). The embedding model sees them as near-identical, so clustering produces one representative.
|
|
140
140
|
|
|
141
141
|
**Diagnostic signal:** `seedCountRaw / seedCount > 10:1` (e.g. 40 raw → 1 selected).
|
|
142
142
|
|
|
@@ -144,9 +144,9 @@ Keep it tight. The operator wakes to a short, decision-ready summary, not a full
|
|
|
144
144
|
|
|
145
145
|
```bash
|
|
146
146
|
cnry discover run <project> \
|
|
147
|
-
--icp-angle "romantic anniversary stay in
|
|
148
|
-
--icp-angle "best rooftop bars and dining hotels
|
|
149
|
-
--icp-angle "walkable
|
|
147
|
+
--icp-angle "romantic anniversary stay in Harborline Beach" \
|
|
148
|
+
--icp-angle "best rooftop bars and dining hotels near Harborline" \
|
|
149
|
+
--icp-angle "walkable Harborline Beach hotels near Kettle Row" \
|
|
150
150
|
--icp-angle "design-forward boutique hotels for creative professionals" \
|
|
151
151
|
--wait
|
|
152
152
|
```
|
|
@@ -262,22 +262,22 @@ Every GBP number belongs to one of three planes. **Tag each figure by plane befo
|
|
|
262
262
|
|
|
263
263
|
## Real-World Data Shapes & Signal Patterns
|
|
264
264
|
|
|
265
|
-
Validated against three live businesses of different types (a computer-support shop, a roofing contractor, and a
|
|
265
|
+
Validated against three live businesses of different types (a computer-support shop, a roofing contractor, and a beachfront boutique hotel). Bake these into any parsing or analysis code.
|
|
266
266
|
|
|
267
267
|
### Response-shape quirks (the parser MUST handle these)
|
|
268
268
|
|
|
269
|
-
- **Values are string-encoded integers.** Keyword counts come as `{ "insightsValue": { "value": "
|
|
269
|
+
- **Values are string-encoded integers.** Keyword counts come as `{ "insightsValue": { "value": "10412" } }` or `{ "insightsValue": { "threshold": "15" } }` — note the nesting under `insightsValue` and that `"10412"` is a string. `Number()` it.
|
|
270
270
|
- **Daily-metric zero days omit the value entirely.** A datedValue with no traffic is `{ "date": {"year":2026,"month":5,"day":1} }` — there is no `"value": "0"`. Treat a missing `value` as 0; don't skip the row.
|
|
271
271
|
- **Dates are split objects** (`{year, month, day}`), not ISO strings. Reassemble.
|
|
272
272
|
|
|
273
273
|
### Signal patterns (what the data actually looks like)
|
|
274
274
|
|
|
275
|
-
- **`BUSINESS_DIRECTION_REQUESTS` is the most reliably-populated conversion signal** across every business type — even a tiny roofing contractor logged
|
|
275
|
+
- **`BUSINESS_DIRECTION_REQUESTS` is the most reliably-populated conversion signal** across every business type — even a tiny roofing contractor logged 58/30d while its website-clicks (2) and call-clicks (1) were near-zero. For local/service businesses it's the headline AEO-conversion proxy, not website clicks.
|
|
276
276
|
- **Most of the 11 daily metrics are all-zero** for non-retail businesses (`BUSINESS_CONVERSATIONS`, `BUSINESS_BOOKINGS`, `BUSINESS_FOOD_*` were 0 for all three). Syncing all 11 is fine (zeros are cheap) but the dashboard should hide all-zero series.
|
|
277
|
-
- **Impressions skew to Maps for physical-destination businesses.** The hotel pulled 7,
|
|
278
|
-
- **Keyword thresholding scales with volume.** A busy hotel was ~89% thresholded (its head terms like `hotels`→10,
|
|
277
|
+
- **Impressions skew to Maps for physical-destination businesses.** The hotel pulled 7,150 desktop-maps impressions vs 2,180 desktop-search in 30 days — people find it on Maps.
|
|
278
|
+
- **Keyword thresholding scales with volume.** A busy hotel was ~89% thresholded (its head terms like `hotels`→10,412 had exact values); both small businesses were **100% thresholded** (every keyword redacted). For the typical SMB location, expect zero exact keyword values — design the UI to lead with the `<N` floor, not exact counts.
|
|
279
279
|
- **An empty Lodging resource is the norm, but it does NOT prove the owner set no amenities.** A real operating hotel returned a lodging resource with only `{ "name": ... }` and zero place-action links, yet its GBP "Hotel details" panel had amenities filled in (breakfast, wifi, parking, accessibility). So `populatedGroupCount === 0` means "canonry can't read structured attributes via this API response", not "the hotel has none". Surface the lodging signal as a **verify** (check the "Hotel details" panel), not a confirmed gap. The place-action emptiness is a separate, genuinely owner-readable signal.
|
|
280
|
-
- **The Places cross-reference is a thin slice for hotels, not the full rendered listing.** Run live against the
|
|
280
|
+
- **The Places cross-reference is a thin slice for hotels, not the full rendered listing.** Run live against the beachfront hotel at the Atmosphere tier, Place Details surfaced exactly one structured amenity, `wheelchair accessibility`, even though the rendered Google hotel module advertises far more (wifi, pool, room service, room rates). Those richer fields come from **Hotel Center**, which the Places API does not expose. So a thin or empty `gbp places` amenity list is NOT evidence the public listing is sparse; Places only carries a narrow, schema-bound subset (breakfast, dining, parking, pet-friendly, accessibility, restroom, family-friendly, outdoor seating, reservations). Read `gbp-listing-discrepancy` as a **floor** on the public-vs-owner gap (proof the listing advertises *at least* the named amenities), never a complete inventory. The owner-control point still stands at any size: even one amenity the profile fails to assert is a structured-data gap the operator can close.
|
|
281
281
|
|
|
282
282
|
## Troubleshooting
|
|
283
283
|
|
|
@@ -792,7 +792,7 @@ import {
|
|
|
792
792
|
wordpressSchemaDeployResultDtoSchema,
|
|
793
793
|
wordpressSchemaStatusResultDtoSchema,
|
|
794
794
|
wordpressStatusDtoSchema
|
|
795
|
-
} from "./chunk-
|
|
795
|
+
} from "./chunk-TOSPZ7I5.js";
|
|
796
796
|
|
|
797
797
|
// src/intelligence-service.ts
|
|
798
798
|
import { eq as eq71, desc as desc32, asc as asc12, and as and60, ne as ne11, or as or18, inArray as inArray27, gte as gte18, lte as lte15, isNull as isNull13, sql as sql28, exists } from "drizzle-orm";
|
|
@@ -4841,7 +4841,7 @@ var MIGRATION_VERSIONS = [
|
|
|
4841
4841
|
// through the LEFT JOIN forces every value to be either a valid `q.id`
|
|
4842
4842
|
// or NULL — pre-existing orphans land with NULL `query_id` / NULL
|
|
4843
4843
|
// `query_text`, preserving the snapshot row instead of failing the
|
|
4844
|
-
// migration.
|
|
4844
|
+
// migration. A May 2026 production DB had 459 such pre-existing
|
|
4845
4845
|
// orphans; without this guard, migrate() throws SQLITE_CONSTRAINT_FOREIGNKEY.
|
|
4846
4846
|
`INSERT INTO query_snapshots_v58 (
|
|
4847
4847
|
id, run_id, query_id, query_text, provider, model, citation_state,
|
|
@@ -4968,7 +4968,7 @@ var MIGRATION_VERSIONS = [
|
|
|
4968
4968
|
version: 63,
|
|
4969
4969
|
name: "audit-log-attribution-columns",
|
|
4970
4970
|
// Adds `user_agent` and `actor_session` to `audit_log` so post-mortems
|
|
4971
|
-
// can attribute destructive events (like the 2026-05-15
|
|
4971
|
+
// can attribute destructive events (like the 2026-05-15
|
|
4972
4972
|
// queries.replaced incident — see PR #593) to a specific caller.
|
|
4973
4973
|
// Without these columns, every mutation rides as `actor='api'` with no
|
|
4974
4974
|
// narrower identity, so it's impossible to tell whether a destructive
|
|
@@ -4,14 +4,14 @@ import {
|
|
|
4
4
|
installSkills,
|
|
5
5
|
loadConfigRaw,
|
|
6
6
|
saveConfigPatch
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-PXVJ3P5G.js";
|
|
8
8
|
import {
|
|
9
9
|
PACKAGE_VERSION
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-JJPZ4OQ2.js";
|
|
11
11
|
import {
|
|
12
12
|
SKILL_MANIFEST_FILENAME,
|
|
13
13
|
SkillsClients
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-TOSPZ7I5.js";
|
|
15
15
|
|
|
16
16
|
// src/skills-autosync.ts
|
|
17
17
|
import fs from "fs";
|
|
@@ -5,12 +5,12 @@ import {
|
|
|
5
5
|
siteCrawlGraphNodes,
|
|
6
6
|
siteCrawlPages,
|
|
7
7
|
siteCrawlSnapshots
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-7YGOE4R2.js";
|
|
9
9
|
import {
|
|
10
10
|
SITE_CRAWL_GRAPH_MAX_EDGES,
|
|
11
11
|
SITE_CRAWL_GRAPH_MAX_NODES,
|
|
12
12
|
TEMPLATE_LINK_RATIO_THRESHOLD
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-TOSPZ7I5.js";
|
|
14
14
|
|
|
15
15
|
// src/site-crawl-graph-layout.ts
|
|
16
16
|
import crypto from "crypto";
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
PACKAGE_VERSION,
|
|
6
6
|
canonryMcpTools,
|
|
7
7
|
isMachineFormat
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-JJPZ4OQ2.js";
|
|
9
9
|
import {
|
|
10
10
|
AGENT_PROVIDER_IDS,
|
|
11
11
|
CANONRY_NPM_PACKAGE_URL,
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
skillsClientSchema,
|
|
38
38
|
upgradeCaveatFor,
|
|
39
39
|
upgradeCommandFor
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-TOSPZ7I5.js";
|
|
41
41
|
|
|
42
42
|
// src/config.ts
|
|
43
43
|
import fs from "fs";
|
|
@@ -13,19 +13,19 @@ import {
|
|
|
13
13
|
resolveOperatorApiKeyIds,
|
|
14
14
|
runWithUsageTags,
|
|
15
15
|
saveConfigPatch
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-PXVJ3P5G.js";
|
|
17
17
|
import {
|
|
18
18
|
deleteSiteCrawlGraphLayout,
|
|
19
19
|
persistSiteCrawlGraphLayout,
|
|
20
20
|
prepareSiteCrawlGraphLayout
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-KHNPBUUN.js";
|
|
22
22
|
import {
|
|
23
23
|
CanonryMcpToolNames,
|
|
24
24
|
CliError,
|
|
25
25
|
PACKAGE_VERSION,
|
|
26
26
|
canonryMcpTools,
|
|
27
27
|
isMachineFormat
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-JJPZ4OQ2.js";
|
|
29
29
|
import {
|
|
30
30
|
CC_CACHE_DIR,
|
|
31
31
|
DUCKDB_SPEC,
|
|
@@ -186,7 +186,7 @@ import {
|
|
|
186
186
|
siteLivenessState,
|
|
187
187
|
toAlertView,
|
|
188
188
|
usageCounters
|
|
189
|
-
} from "./chunk-
|
|
189
|
+
} from "./chunk-7YGOE4R2.js";
|
|
190
190
|
import {
|
|
191
191
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
192
192
|
AGENT_NONE,
|
|
@@ -223,6 +223,8 @@ import {
|
|
|
223
223
|
RUN_FILL_PROVIDER_BREAKER,
|
|
224
224
|
ResearchQueryStatuses,
|
|
225
225
|
ResearchRunStatuses,
|
|
226
|
+
RetrievalContracts,
|
|
227
|
+
RetrievalStatuses,
|
|
226
228
|
RunKinds,
|
|
227
229
|
RunStatuses,
|
|
228
230
|
RunTriggers,
|
|
@@ -351,7 +353,7 @@ import {
|
|
|
351
353
|
visibilityReportRequestSchema,
|
|
352
354
|
winnabilityClassLabel,
|
|
353
355
|
withRetry
|
|
354
|
-
} from "./chunk-
|
|
356
|
+
} from "./chunk-TOSPZ7I5.js";
|
|
355
357
|
|
|
356
358
|
// src/runtime-context.ts
|
|
357
359
|
function cliRuntimeContext(env = process.env, stdio = process) {
|
|
@@ -4076,6 +4078,7 @@ async function withRetry3(fn, options = {}) {
|
|
|
4076
4078
|
|
|
4077
4079
|
// ../provider-openai/src/normalize.ts
|
|
4078
4080
|
var DEFAULT_MODEL2 = "gpt-5.4";
|
|
4081
|
+
var OPENAI_RETRIEVAL_CONTRACT = RetrievalContracts["search-required-v1"];
|
|
4079
4082
|
function createClient3(config) {
|
|
4080
4083
|
return new OpenAI({
|
|
4081
4084
|
apiKey: config.apiKey,
|
|
@@ -4138,6 +4141,8 @@ async function executeTrackedQuery2(input) {
|
|
|
4138
4141
|
() => client.responses.create({
|
|
4139
4142
|
model,
|
|
4140
4143
|
tools: [webSearchTool],
|
|
4144
|
+
// search-required-v1: web_search is the only tool, so requiring a tool
|
|
4145
|
+
// call requires a search. See OPENAI_RETRIEVAL_CONTRACT.
|
|
4141
4146
|
tool_choice: "required",
|
|
4142
4147
|
input: buildPrompt2(input.query)
|
|
4143
4148
|
})
|
|
@@ -4150,7 +4155,9 @@ async function executeTrackedQuery2(input) {
|
|
|
4150
4155
|
model,
|
|
4151
4156
|
servedModel: extractServedModel2(rawResponse),
|
|
4152
4157
|
groundingSources: parsed.groundingSources,
|
|
4153
|
-
searchQueries: parsed.searchQueries
|
|
4158
|
+
searchQueries: parsed.searchQueries,
|
|
4159
|
+
retrievalStatus: parsed.retrievalStatus,
|
|
4160
|
+
retrievalContract: OPENAI_RETRIEVAL_CONTRACT
|
|
4154
4161
|
};
|
|
4155
4162
|
} catch (err) {
|
|
4156
4163
|
const msg = describeError(err);
|
|
@@ -4168,7 +4175,8 @@ function normalizeResult2(raw) {
|
|
|
4168
4175
|
answerText: parsed.answerText,
|
|
4169
4176
|
citedDomains,
|
|
4170
4177
|
groundingSources,
|
|
4171
|
-
searchQueries
|
|
4178
|
+
searchQueries,
|
|
4179
|
+
retrievalStatus: useParsed ? parsed.retrievalStatus : raw.retrievalStatus
|
|
4172
4180
|
};
|
|
4173
4181
|
}
|
|
4174
4182
|
function hasParsedResponseContent2(rawResponse) {
|
|
@@ -4185,9 +4193,22 @@ function reparseStoredResult2(rawResponse) {
|
|
|
4185
4193
|
answerText: extractAnswerTextFromRaw(rawResponse),
|
|
4186
4194
|
citedDomains: extractCitedDomainsFromSources2(groundingSources),
|
|
4187
4195
|
groundingSources,
|
|
4188
|
-
searchQueries
|
|
4196
|
+
searchQueries,
|
|
4197
|
+
retrievalStatus: extractRetrievalStatusFromRaw(rawResponse)
|
|
4189
4198
|
};
|
|
4190
4199
|
}
|
|
4200
|
+
function extractRetrievalStatusFromRaw(rawResponse) {
|
|
4201
|
+
const output = rawResponse.output;
|
|
4202
|
+
if (!Array.isArray(output) || output.length === 0) return RetrievalStatuses.unknown;
|
|
4203
|
+
const searched = output.some(
|
|
4204
|
+
(item) => item !== null && typeof item === "object" && item.type === "web_search_call"
|
|
4205
|
+
);
|
|
4206
|
+
if (searched) return RetrievalStatuses.used;
|
|
4207
|
+
if (typeof rawResponse.status === "string" && rawResponse.status !== "completed") {
|
|
4208
|
+
return RetrievalStatuses.unknown;
|
|
4209
|
+
}
|
|
4210
|
+
return RetrievalStatuses["not-used"];
|
|
4211
|
+
}
|
|
4191
4212
|
function buildPrompt2(query) {
|
|
4192
4213
|
return query;
|
|
4193
4214
|
}
|
|
@@ -4402,13 +4423,12 @@ var openaiAdapter = {
|
|
|
4402
4423
|
servedModel: raw.servedModel,
|
|
4403
4424
|
groundingSources: raw.groundingSources,
|
|
4404
4425
|
searchQueries: raw.searchQueries,
|
|
4405
|
-
//
|
|
4406
|
-
//
|
|
4407
|
-
//
|
|
4408
|
-
//
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
retrievalContract: "native-auto-v1"
|
|
4426
|
+
// Read from the response's `web_search_call` items. Under forced search
|
|
4427
|
+
// nearly every row reads `used`; that is the contract holding, and a
|
|
4428
|
+
// `not-used` row is the visible breach, so the observation still
|
|
4429
|
+
// discriminates. The contract declares how the request was built.
|
|
4430
|
+
retrievalStatus: raw.retrievalStatus,
|
|
4431
|
+
retrievalContract: raw.retrievalContract
|
|
4412
4432
|
};
|
|
4413
4433
|
},
|
|
4414
4434
|
normalizeResult(raw) {
|
|
@@ -4417,7 +4437,11 @@ var openaiAdapter = {
|
|
|
4417
4437
|
rawResponse: raw.rawResponse,
|
|
4418
4438
|
model: raw.model,
|
|
4419
4439
|
groundingSources: raw.groundingSources,
|
|
4420
|
-
searchQueries: raw.searchQueries
|
|
4440
|
+
searchQueries: raw.searchQueries,
|
|
4441
|
+
// A reconstruction that predates the field falls back to `unknown`
|
|
4442
|
+
// rather than asserting an absence.
|
|
4443
|
+
retrievalStatus: raw.retrievalStatus ?? RetrievalStatuses.unknown,
|
|
4444
|
+
retrievalContract: raw.retrievalContract ?? OPENAI_RETRIEVAL_CONTRACT
|
|
4421
4445
|
};
|
|
4422
4446
|
const normalized = normalizeResult2(openaiRaw);
|
|
4423
4447
|
return {
|
|
@@ -4426,7 +4450,7 @@ var openaiAdapter = {
|
|
|
4426
4450
|
citedDomains: normalized.citedDomains,
|
|
4427
4451
|
groundingSources: normalized.groundingSources,
|
|
4428
4452
|
searchQueries: normalized.searchQueries,
|
|
4429
|
-
retrievalStatus:
|
|
4453
|
+
retrievalStatus: normalized.retrievalStatus
|
|
4430
4454
|
};
|
|
4431
4455
|
},
|
|
4432
4456
|
async generateText(prompt, config) {
|
|
@@ -4591,7 +4615,7 @@ function reparseStoredResult3(rawResponse) {
|
|
|
4591
4615
|
citedDomains: extractCitedDomainsFromSources3(groundingSources),
|
|
4592
4616
|
groundingSources,
|
|
4593
4617
|
searchQueries,
|
|
4594
|
-
retrievalStatus:
|
|
4618
|
+
retrievalStatus: extractRetrievalStatusFromRaw2(rawResponse),
|
|
4595
4619
|
...providerErrors.length > 0 ? { providerError: `web_search tool error: ${providerErrors.join(", ")}` } : {}
|
|
4596
4620
|
};
|
|
4597
4621
|
}
|
|
@@ -4631,7 +4655,7 @@ function extractGroundingSourcesFromRaw2(rawResponse) {
|
|
|
4631
4655
|
}
|
|
4632
4656
|
return sources;
|
|
4633
4657
|
}
|
|
4634
|
-
function
|
|
4658
|
+
function extractRetrievalStatusFromRaw2(rawResponse) {
|
|
4635
4659
|
try {
|
|
4636
4660
|
const content = rawResponse.content;
|
|
4637
4661
|
if (!Array.isArray(content) || content.length === 0) return "unknown";
|
|
@@ -6604,7 +6628,7 @@ function nonBlank(value) {
|
|
|
6604
6628
|
return trimmed ? trimmed : void 0;
|
|
6605
6629
|
}
|
|
6606
6630
|
function resolveBuildCommit(env = process.env) {
|
|
6607
|
-
const embedded = true ? "
|
|
6631
|
+
const embedded = true ? "b00090f58284f63906eba753cace2b96a166ccfd" : void 0;
|
|
6608
6632
|
return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
|
|
6609
6633
|
}
|
|
6610
6634
|
function resolveInstanceIdentity(env = process.env) {
|
|
@@ -11720,6 +11744,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
11720
11744
|
let mentioned = 0;
|
|
11721
11745
|
let reparsed = 0;
|
|
11722
11746
|
let providerErrors = 0;
|
|
11747
|
+
let retrievalRelabeled = 0;
|
|
11723
11748
|
if (scopedProjects.length > 0) {
|
|
11724
11749
|
const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId, planVersionId: runs.measurementPlanVersionId }).from(runs).where(and13(
|
|
11725
11750
|
eq15(runs.kind, RunKinds["answer-visibility"]),
|
|
@@ -11758,7 +11783,9 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
11758
11783
|
citedDomains: querySnapshots.citedDomains,
|
|
11759
11784
|
competitorOverlap: querySnapshots.competitorOverlap,
|
|
11760
11785
|
recommendedCompetitors: querySnapshots.recommendedCompetitors,
|
|
11761
|
-
rawResponse: querySnapshots.rawResponse
|
|
11786
|
+
rawResponse: querySnapshots.rawResponse,
|
|
11787
|
+
retrievalStatus: querySnapshots.retrievalStatus,
|
|
11788
|
+
retrievalContract: querySnapshots.retrievalContract
|
|
11762
11789
|
}).from(querySnapshots).where(inArray6(querySnapshots.runId, batchRunIds)).all();
|
|
11763
11790
|
const pendingUpdates = [];
|
|
11764
11791
|
for (const snapshot of snapshotRows) {
|
|
@@ -11783,10 +11810,9 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
11783
11810
|
citedDomains: reparsedResult.citedDomains,
|
|
11784
11811
|
groundingSources: reparsedResult.groundingSources,
|
|
11785
11812
|
searchQueries: reparsedResult.searchQueries,
|
|
11786
|
-
//
|
|
11787
|
-
//
|
|
11788
|
-
//
|
|
11789
|
-
// reconstruction from asserting anything about a search.
|
|
11813
|
+
// Only feeds the citation/mention helpers below, which ignore
|
|
11814
|
+
// it. Retrieval is written back solely by the narrow OpenAI
|
|
11815
|
+
// contract correction after this block.
|
|
11790
11816
|
retrievalStatus: "unknown"
|
|
11791
11817
|
};
|
|
11792
11818
|
const nextCitationState = determineCitationState(normalized, projectDomains);
|
|
@@ -11821,6 +11847,11 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
11821
11847
|
nextPatch.rawResponse = nextRawResponse;
|
|
11822
11848
|
}
|
|
11823
11849
|
}
|
|
11850
|
+
const retrievalCorrection = correctStoredOpenAIRetrieval(snapshot, reparsedResult);
|
|
11851
|
+
if (retrievalCorrection) {
|
|
11852
|
+
retrievalRelabeled++;
|
|
11853
|
+
Object.assign(nextPatch, retrievalCorrection);
|
|
11854
|
+
}
|
|
11824
11855
|
if (Object.keys(nextPatch).length > 0) {
|
|
11825
11856
|
pendingUpdates.push({ id: snapshot.id, patch: nextPatch });
|
|
11826
11857
|
}
|
|
@@ -11847,7 +11878,8 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
11847
11878
|
updated,
|
|
11848
11879
|
mentioned,
|
|
11849
11880
|
reparsed,
|
|
11850
|
-
providerErrors
|
|
11881
|
+
providerErrors,
|
|
11882
|
+
retrievalRelabeled
|
|
11851
11883
|
};
|
|
11852
11884
|
if (isDryRun) {
|
|
11853
11885
|
result2.dryRun = true;
|
|
@@ -11872,6 +11904,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
11872
11904
|
console.log(` Mentioned: ${mentioned}`);
|
|
11873
11905
|
console.log(` Reparsed: ${reparsed}`);
|
|
11874
11906
|
console.log(` Errors: ${providerErrors}`);
|
|
11907
|
+
console.log(` Relabeled: ${retrievalRelabeled} (OpenAI retrieval contract)`);
|
|
11875
11908
|
if (isDryRun) {
|
|
11876
11909
|
console.log(`
|
|
11877
11910
|
No DB writes performed. Re-run without --dry-run to apply.`);
|
|
@@ -12072,7 +12105,8 @@ function backfillProjectAnswerMentions(db, projectId, opts) {
|
|
|
12072
12105
|
answerText,
|
|
12073
12106
|
citedDomains,
|
|
12074
12107
|
groundingSources,
|
|
12075
|
-
//
|
|
12108
|
+
// Only feeds the competitor helpers, which ignore it. This pass never
|
|
12109
|
+
// observes or writes retrieval.
|
|
12076
12110
|
retrievalStatus: "unknown",
|
|
12077
12111
|
searchQueries: []
|
|
12078
12112
|
};
|
|
@@ -12181,7 +12215,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
12181
12215
|
return result2;
|
|
12182
12216
|
}
|
|
12183
12217
|
async function backfillInsightsCommand(project, opts) {
|
|
12184
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
12218
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-H4LGPJLZ.js");
|
|
12185
12219
|
const config = loadConfig();
|
|
12186
12220
|
const db = createClient(config.database);
|
|
12187
12221
|
migrate(db);
|
|
@@ -12470,6 +12504,14 @@ Snapshot attribution ${isDryRun ? "preview" : "recovery"} complete.`);
|
|
|
12470
12504
|
No DB writes performed. Re-run without --dry-run to apply.`);
|
|
12471
12505
|
}
|
|
12472
12506
|
}
|
|
12507
|
+
function correctStoredOpenAIRetrieval(snapshot, reparsed) {
|
|
12508
|
+
if (snapshot.provider !== ProviderNames.openai) return null;
|
|
12509
|
+
if (snapshot.retrievalContract !== RetrievalContracts["native-auto-v1"]) return null;
|
|
12510
|
+
return {
|
|
12511
|
+
retrievalContract: OPENAI_RETRIEVAL_CONTRACT,
|
|
12512
|
+
...reparsed?.retrievalStatus === void 0 ? {} : { retrievalStatus: reparsed.retrievalStatus }
|
|
12513
|
+
};
|
|
12514
|
+
}
|
|
12473
12515
|
function reparseProviderSnapshot(provider, rawResponse) {
|
|
12474
12516
|
const envelope = parseJsonColumn(rawResponse, {});
|
|
12475
12517
|
const apiResponse = resolveStoredApiResponse(envelope);
|
|
@@ -19737,6 +19737,8 @@ export {
|
|
|
19737
19737
|
buildMeasurementRunManifestV1,
|
|
19738
19738
|
measurementReportResponseSchema,
|
|
19739
19739
|
measurementPropertyEvidenceResponseSchema,
|
|
19740
|
+
RetrievalStatuses,
|
|
19741
|
+
RetrievalContracts,
|
|
19740
19742
|
MEASUREMENT_PORTFOLIO_DEFAULT_LIMIT,
|
|
19741
19743
|
MEASUREMENT_PORTFOLIO_ROW_EVIDENCE_LIMIT,
|
|
19742
19744
|
MEASUREMENT_PORTFOLIO_ANSWER_SOURCES_LIMIT,
|
package/dist/cli.js
CHANGED
|
@@ -35,11 +35,11 @@ import {
|
|
|
35
35
|
trackCliCommandFinished,
|
|
36
36
|
trackEvent,
|
|
37
37
|
waitForServerRuntimeStartup
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-RV3PYKXP.js";
|
|
39
39
|
import {
|
|
40
40
|
autoSyncSkills,
|
|
41
41
|
formatAutoSyncNotice
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-CVUR3O5G.js";
|
|
43
43
|
import {
|
|
44
44
|
configExists,
|
|
45
45
|
createApiClient,
|
|
@@ -56,13 +56,13 @@ import {
|
|
|
56
56
|
saveConfig,
|
|
57
57
|
saveConfigPatch,
|
|
58
58
|
shouldPrintServeSkillsNudge
|
|
59
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-PXVJ3P5G.js";
|
|
60
60
|
import {
|
|
61
61
|
DEFAULT_DEMO_TRUSTED_PROXIES,
|
|
62
62
|
trustedProxiesProblem,
|
|
63
63
|
trustedProxyProblem
|
|
64
64
|
} from "./chunk-RB2F4D3Z.js";
|
|
65
|
-
import "./chunk-
|
|
65
|
+
import "./chunk-KHNPBUUN.js";
|
|
66
66
|
import {
|
|
67
67
|
CliError,
|
|
68
68
|
EXIT_SYSTEM_ERROR,
|
|
@@ -74,7 +74,7 @@ import {
|
|
|
74
74
|
runMeasurementDraftAction,
|
|
75
75
|
systemError,
|
|
76
76
|
usageError
|
|
77
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-JJPZ4OQ2.js";
|
|
78
78
|
import {
|
|
79
79
|
CLOUDFLARE_WORKER_BINDINGS,
|
|
80
80
|
CLOUDFLARE_WORKER_GENERATED_MARKER,
|
|
@@ -89,7 +89,7 @@ import {
|
|
|
89
89
|
queries,
|
|
90
90
|
renderReportHtml,
|
|
91
91
|
runs
|
|
92
|
-
} from "./chunk-
|
|
92
|
+
} from "./chunk-7YGOE4R2.js";
|
|
93
93
|
import {
|
|
94
94
|
AdsDeliverySnapshotStatuses,
|
|
95
95
|
AdsHistoricalCampaignRollupStatuses,
|
|
@@ -177,7 +177,7 @@ import {
|
|
|
177
177
|
snapshotProviderModeSchema,
|
|
178
178
|
visibilityReportRequestSchema,
|
|
179
179
|
winnabilityClassSchema
|
|
180
|
-
} from "./chunk-
|
|
180
|
+
} from "./chunk-TOSPZ7I5.js";
|
|
181
181
|
|
|
182
182
|
// src/cli.ts
|
|
183
183
|
import { pathToFileURL } from "url";
|
|
@@ -442,7 +442,7 @@ function parseDemoTrustedProxies(values) {
|
|
|
442
442
|
async function demoCommand(options) {
|
|
443
443
|
const { host, port } = parseDemoListenOptions(options);
|
|
444
444
|
const trustProxy = parseDemoTrustedProxies(options.trustProxy);
|
|
445
|
-
const { createDemoServer } = await import("./demo-server-
|
|
445
|
+
const { createDemoServer } = await import("./demo-server-JYDAUEP2.js");
|
|
446
446
|
const app = await createDemoServer({ trustProxy });
|
|
447
447
|
let stopping = false;
|
|
448
448
|
const stop = () => {
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
layoutSiteCrawlGraphInput,
|
|
6
6
|
persistSiteCrawlGraphLayout
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-KHNPBUUN.js";
|
|
8
8
|
import {
|
|
9
9
|
PACKAGE_VERSION,
|
|
10
10
|
canonryMcpTools
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-JJPZ4OQ2.js";
|
|
12
12
|
import {
|
|
13
13
|
HISTORY_WINDOW_RUNS,
|
|
14
14
|
IntelligenceService,
|
|
@@ -84,7 +84,7 @@ import {
|
|
|
84
84
|
siteCrawlPages,
|
|
85
85
|
siteCrawlSnapshots,
|
|
86
86
|
trafficSources
|
|
87
|
-
} from "./chunk-
|
|
87
|
+
} from "./chunk-7YGOE4R2.js";
|
|
88
88
|
import {
|
|
89
89
|
RunKinds,
|
|
90
90
|
SiteCrawlFetchStates,
|
|
@@ -108,7 +108,7 @@ import {
|
|
|
108
108
|
placementLinkDecision,
|
|
109
109
|
siteAuditPageFactorSchema,
|
|
110
110
|
wilsonInterval
|
|
111
|
-
} from "./chunk-
|
|
111
|
+
} from "./chunk-TOSPZ7I5.js";
|
|
112
112
|
|
|
113
113
|
// src/demo-server.ts
|
|
114
114
|
import { dirname, join as join2 } from "path";
|
package/dist/index.js
CHANGED
|
@@ -3,14 +3,14 @@ import {
|
|
|
3
3
|
createGoogleMarketingCredentialStore,
|
|
4
4
|
createGoogleMarketingRuntime,
|
|
5
5
|
createServer
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-RV3PYKXP.js";
|
|
7
7
|
import {
|
|
8
8
|
loadConfig
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-PXVJ3P5G.js";
|
|
10
|
+
import "./chunk-KHNPBUUN.js";
|
|
11
|
+
import "./chunk-JJPZ4OQ2.js";
|
|
12
|
+
import "./chunk-7YGOE4R2.js";
|
|
13
|
+
import "./chunk-TOSPZ7I5.js";
|
|
14
14
|
export {
|
|
15
15
|
GoogleMarketingRuntimeError,
|
|
16
16
|
createGoogleMarketingCredentialStore,
|
package/dist/mcp.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
autoSyncSkills
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-CVUR3O5G.js";
|
|
4
4
|
import {
|
|
5
5
|
createApiClient,
|
|
6
6
|
createCanonryMcpServer
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-PXVJ3P5G.js";
|
|
8
|
+
import "./chunk-JJPZ4OQ2.js";
|
|
9
9
|
import {
|
|
10
10
|
isReadOnlyKey,
|
|
11
11
|
updateCheckEnvOptOut
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-TOSPZ7I5.js";
|
|
13
13
|
|
|
14
14
|
// src/mcp/cli.ts
|
|
15
15
|
import { randomUUID } from "crypto";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonry/canonry",
|
|
3
|
-
"version": "5.20.
|
|
3
|
+
"version": "5.20.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Self-hosted AI visibility (AEO) platform: track how ChatGPT, Claude, Gemini, and Perplexity cite your domain, join it with Search Console, GA4, server-side traffic, and paid media, and fix what you find through agent tools (CLI, REST, MCP). Local SQLite.",
|
|
6
6
|
"keywords": [
|
|
@@ -89,29 +89,29 @@
|
|
|
89
89
|
"@ainyc/canonry-web": "0.0.0",
|
|
90
90
|
"@ainyc/canonry-api-routes": "0.0.0",
|
|
91
91
|
"@ainyc/canonry-api-client": "0.0.0",
|
|
92
|
-
"@ainyc/canonry-contracts": "0.0.0",
|
|
93
92
|
"@ainyc/canonry-config": "0.0.0",
|
|
93
|
+
"@ainyc/canonry-contracts": "0.0.0",
|
|
94
94
|
"@ainyc/canonry-db": "0.0.0",
|
|
95
95
|
"@ainyc/canonry-integration-bing": "0.0.0",
|
|
96
96
|
"@ainyc/canonry-integration-cloud-run": "0.0.0",
|
|
97
97
|
"@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
|
|
98
|
-
"@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
|
|
99
98
|
"@ainyc/canonry-integration-commoncrawl": "0.0.0",
|
|
100
99
|
"@ainyc/canonry-integration-google-ads": "0.0.0",
|
|
101
100
|
"@ainyc/canonry-integration-google-business-profile": "0.0.0",
|
|
101
|
+
"@ainyc/canonry-integration-google": "0.0.0",
|
|
102
|
+
"@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
|
|
103
|
+
"@ainyc/canonry-integration-google-places": "0.0.0",
|
|
102
104
|
"@ainyc/canonry-integration-google-tag-manager": "0.0.0",
|
|
103
105
|
"@ainyc/canonry-integration-traffic": "0.0.0",
|
|
104
|
-
"@ainyc/canonry-integration-openai-ads": "0.0.0",
|
|
105
|
-
"@ainyc/canonry-integration-google": "0.0.0",
|
|
106
106
|
"@ainyc/canonry-integration-wordpress": "0.0.0",
|
|
107
|
-
"@ainyc/canonry-intelligence": "0.0.0",
|
|
108
|
-
"@ainyc/canonry-integration-google-places": "0.0.0",
|
|
109
|
-
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
110
107
|
"@ainyc/canonry-provider-cdp": "0.0.0",
|
|
111
108
|
"@ainyc/canonry-provider-gemini": "0.0.0",
|
|
109
|
+
"@ainyc/canonry-integration-openai-ads": "0.0.0",
|
|
110
|
+
"@ainyc/canonry-intelligence": "0.0.0",
|
|
112
111
|
"@ainyc/canonry-provider-local": "0.0.0",
|
|
113
112
|
"@ainyc/canonry-provider-perplexity": "0.0.0",
|
|
114
|
-
"@ainyc/canonry-provider-openai": "0.0.0"
|
|
113
|
+
"@ainyc/canonry-provider-openai": "0.0.0",
|
|
114
|
+
"@ainyc/canonry-provider-claude": "0.0.0"
|
|
115
115
|
},
|
|
116
116
|
"scripts": {
|
|
117
117
|
"build": "pnpm run build:cli && pnpm run build:web",
|