@ainyc/canonry 4.93.0 → 4.95.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/assets/agent-workspace/skills/canonry/SKILL.md +1 -1
  2. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +6 -2
  3. package/assets/agent-workspace/skills/canonry/references/google-business-profile.md +9 -9
  4. package/assets/assets/{BacklinksPage-nkgAbbjk.js → BacklinksPage-CT3DfxMw.js} +1 -1
  5. package/assets/assets/{ChartPrimitives-kdfylr1f.js → ChartPrimitives-B6Dleq3z.js} +1 -1
  6. package/assets/assets/ProjectPage-DeEE66hg.js +6 -0
  7. package/assets/assets/{RunRow-P-n5OkiY.js → RunRow-C7rEdPSI.js} +1 -1
  8. package/assets/assets/{RunsPage-DfBb4WCS.js → RunsPage-ByohAHOY.js} +1 -1
  9. package/assets/assets/{SettingsPage-CRGuFgeK.js → SettingsPage-D4dwPwZy.js} +1 -1
  10. package/assets/assets/{TrafficPage-CMqP0AGZ.js → TrafficPage-Kh1CSxMr.js} +1 -1
  11. package/assets/assets/{TrafficSourceDetailPage-XRGzkKWk.js → TrafficSourceDetailPage-CIlOFRYB.js} +1 -1
  12. package/assets/assets/{arrow-left-BCR55g4n.js → arrow-left-BoX1Vp7B.js} +1 -1
  13. package/assets/assets/{extract-error-message-BG1AlucT.js → extract-error-message-ZqsMkFg8.js} +1 -1
  14. package/assets/assets/{index-BPjIZIDO.js → index-k1XIw8WK.js} +3 -3
  15. package/assets/assets/{trash-2-Cme1gNjU.js → trash-2-A1PQNK3E.js} +1 -1
  16. package/assets/index.html +1 -1
  17. package/dist/{chunk-ADTLQHJY.js → chunk-4E4JPS37.js} +18 -6
  18. package/dist/{chunk-KBDQ5OE4.js → chunk-7YU6ILWL.js} +49 -15
  19. package/dist/{chunk-AUQNKEHV.js → chunk-BUGJOQNW.js} +6 -1
  20. package/dist/{chunk-PK46TKGM.js → chunk-FFSJRZJR.js} +7 -4
  21. package/dist/cli.js +27 -11
  22. package/dist/index.d.ts +9 -1
  23. package/dist/index.js +4 -4
  24. package/dist/{intelligence-service-4ATCJITP.js → intelligence-service-PGEGDNY6.js} +2 -2
  25. package/dist/mcp.js +2 -2
  26. package/package.json +9 -9
  27. package/assets/assets/ProjectPage-BQYmq7FP.js +0 -6
@@ -1 +1 @@
1
- import{c}from"./index-BPjIZIDO.js";const a=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],h=c("circle-check",a);const e=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],n=c("circle-question-mark",e);const o=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],s=c("download",o);const t=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],r=c("trash-2",t);export{h as C,s as D,r as T,n as a};
1
+ import{c}from"./index-k1XIw8WK.js";const a=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],h=c("circle-check",a);const e=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],n=c("circle-question-mark",e);const o=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],s=c("download",o);const t=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],r=c("trash-2",t);export{h as C,s as D,r as T,n as a};
package/assets/index.html CHANGED
@@ -12,7 +12,7 @@
12
12
  <link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
13
13
  <link rel="apple-touch-icon" href="./apple-touch-icon.png" />
14
14
  <title>Canonry</title>
15
- <script type="module" crossorigin src="./assets/index-BPjIZIDO.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-k1XIw8WK.js"></script>
16
16
  <link rel="modulepreload" crossorigin href="./assets/vendor-tanstack-Dq7p98wZ.js">
17
17
  <link rel="modulepreload" crossorigin href="./assets/vendor-radix-B57xfQbP.js">
18
18
  <link rel="modulepreload" crossorigin href="./assets/vendor-recharts-ClRVR6aX.js">
@@ -9,7 +9,7 @@ import {
9
9
  loadConfig,
10
10
  loadConfigRaw,
11
11
  saveConfigPatch
12
- } from "./chunk-PK46TKGM.js";
12
+ } from "./chunk-FFSJRZJR.js";
13
13
  import {
14
14
  CC_CACHE_DIR,
15
15
  DUCKDB_SPEC,
@@ -111,7 +111,7 @@ import {
111
111
  siteAuditPages,
112
112
  siteAuditSnapshots,
113
113
  usageCounters
114
- } from "./chunk-KBDQ5OE4.js";
114
+ } from "./chunk-7YU6ILWL.js";
115
115
  import {
116
116
  AGENT_MEMORY_VALUE_MAX_BYTES,
117
117
  AGENT_PROVIDER_IDS,
@@ -171,7 +171,7 @@ import {
171
171
  validationError,
172
172
  winnabilityClassLabel,
173
173
  withRetry
174
- } from "./chunk-AUQNKEHV.js";
174
+ } from "./chunk-BUGJOQNW.js";
175
175
 
176
176
  // src/telemetry.ts
177
177
  import crypto from "crypto";
@@ -2767,6 +2767,15 @@ function normalizeViews(raw) {
2767
2767
  return out.length > 0 ? out : void 0;
2768
2768
  }
2769
2769
 
2770
+ // src/agent-config.ts
2771
+ function resolveAgentEnabled(env, config) {
2772
+ const raw = env.CANONRY_AGENT_DISABLED?.trim();
2773
+ if (raw) {
2774
+ return !(raw === "1" || raw.toLowerCase() === "true");
2775
+ }
2776
+ return config.agent?.mode !== "disabled";
2777
+ }
2778
+
2770
2779
  // src/places-config.ts
2771
2780
  var DEFAULT_TIER = "atmosphere";
2772
2781
  var DEFAULT_REFRESH_INTERVAL_DAYS = 7;
@@ -6387,7 +6396,7 @@ function readStoredGroundingSources(rawResponse) {
6387
6396
  return result;
6388
6397
  }
6389
6398
  async function backfillInsightsCommand(project, opts) {
6390
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-4ATCJITP.js");
6399
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-PGEGDNY6.js");
6391
6400
  const config = loadConfig();
6392
6401
  const db = createClient(config.database);
6393
6402
  migrate(db);
@@ -10333,17 +10342,19 @@ async function createServer(opts) {
10333
10342
  const notifier = new Notifier(opts.db, serverUrl);
10334
10343
  const intelligenceService = new IntelligenceService(opts.db);
10335
10344
  const aeroClient = new ApiClient(opts.config.apiUrl, opts.config.apiKey, { skipProbe: true });
10336
- const sessionRegistry = new SessionRegistry({
10345
+ const agentEnabled = resolveAgentEnabled(process.env, opts.config);
10346
+ const sessionRegistry = agentEnabled ? new SessionRegistry({
10337
10347
  db: opts.db,
10338
10348
  client: aeroClient,
10339
10349
  config: opts.config
10340
- });
10350
+ }) : void 0;
10341
10351
  const runCoordinator = new RunCoordinator(
10342
10352
  opts.db,
10343
10353
  notifier,
10344
10354
  intelligenceService,
10345
10355
  (runId, projectId, result) => notifier.dispatchInsightWebhooks(runId, projectId, result),
10346
10356
  async (ctx) => {
10357
+ if (!sessionRegistry) return;
10347
10358
  const project = opts.db.select({ name: projects.name }).from(projects).where(eq20(projects.id, ctx.projectId)).get();
10348
10359
  if (!project) return;
10349
10360
  let content;
@@ -10870,6 +10881,7 @@ async function createServer(opts) {
10870
10881
  // Local-only Aero agent routes. Registered here so they inherit api-routes'
10871
10882
  // auth plugin — bare `registerAgentRoutes(app, ...)` would skip auth.
10872
10883
  registerAuthenticatedRoutes: async (scope) => {
10884
+ if (!sessionRegistry) return;
10873
10885
  registerAgentRoutes(scope, { db: opts.db, sessionRegistry });
10874
10886
  },
10875
10887
  getGoogleAuthConfig: () => getGoogleAuthConfig(opts.config),
@@ -259,7 +259,7 @@ import {
259
259
  wordpressSchemaDeployResultDtoSchema,
260
260
  wordpressSchemaStatusResultDtoSchema,
261
261
  wordpressStatusDtoSchema
262
- } from "./chunk-AUQNKEHV.js";
262
+ } from "./chunk-BUGJOQNW.js";
263
263
 
264
264
  // src/intelligence-service.ts
265
265
  import { eq as eq37, desc as desc18, asc as asc5, and as and27, ne as ne5, or as or5, inArray as inArray14, gte as gte7, lte as lte4 } from "drizzle-orm";
@@ -5284,7 +5284,7 @@ function analyzeGbp(signals) {
5284
5284
  title: `${loc.displayName}: public listing advertises ${loc.placesAmenities.length} amenit${loc.placesAmenities.length === 1 ? "y" : "ies"} canonry can\u2019t confirm via the GBP API`,
5285
5285
  recommendation: {
5286
5286
  action: 'Verify these amenities are set in the Google Business Profile "Hotel details" panel so the structured profile matches what the public listing advertises.',
5287
- reason: `Google\u2019s rendered listing advertises ${amenityList}, but the GBP Lodging API returns no structured attributes for this location. The Lodging API does not expose the owner-set "Hotel details" amenity panel, so the amenities may already be set there and simply not be readable via the API. Verify in Hotel details: if any are missing, add them, since the structured attributes are the amenity data you directly control and that AI answer engines cite.`
5287
+ reason: `Google\u2019s rendered listing advertises ${amenityList}, but the GBP Lodging API returned no readable structured groups for this location. The amenities may already be set in the owner-facing "Hotel details" panel and simply not be visible in this API response. Verify in Hotel details: if any are missing, add them, since those structured attributes are amenity data you directly control.`
5288
5288
  }
5289
5289
  });
5290
5290
  } else {
@@ -5295,7 +5295,7 @@ function analyzeGbp(signals) {
5295
5295
  title: `${loc.displayName}: structured lodging attributes not readable via the GBP API`,
5296
5296
  recommendation: {
5297
5297
  action: 'Verify the hotel amenities in the Google Business Profile "Hotel details" panel. If they are already set there, no change is needed.',
5298
- reason: 'The GBP Lodging API returns no structured attributes for this location. That resource is commonly empty even for complete hotels, because the owner-set "Hotel details" amenity panel (breakfast, wifi, parking, accessibility, and the like) writes to a separate surface the Lodging API does not expose. Treat this as a verify, not a confirmed gap: confirm the amenities are set in Hotel details, the amenity source you directly control and that AI answer engines cite.'
5298
+ reason: 'The GBP Lodging API returned no readable structured groups for this location. That response can be empty even for complete hotels whose owner-facing "Hotel details" panel has amenities set. Treat this as a verify, not a confirmed gap: confirm the amenities are set in Hotel details.'
5299
5299
  }
5300
5300
  });
5301
5301
  }
@@ -8117,7 +8117,15 @@ function buildCategoryCounts(counts2) {
8117
8117
  }
8118
8118
 
8119
8119
  // ../api-routes/src/intelligence.ts
8120
- import { eq as eq11, desc as desc4, and as and6, inArray as inArray4 } from "drizzle-orm";
8120
+ import { eq as eq11, desc as desc4, and as and6, inArray as inArray4, like } from "drizzle-orm";
8121
+ var SEVERITY_RANK = { low: 0, medium: 1, high: 2, critical: 3 };
8122
+ function severitiesAtOrAbove(min) {
8123
+ const floor = SEVERITY_RANK[min];
8124
+ if (floor === void 0) {
8125
+ throw validationError(`Invalid severity "${min}". Use one of: low, medium, high, critical.`);
8126
+ }
8127
+ return Object.keys(SEVERITY_RANK).filter((s) => SEVERITY_RANK[s] >= floor);
8128
+ }
8121
8129
  function emptyHealthSnapshot(projectId) {
8122
8130
  return {
8123
8131
  id: `no-data:${projectId}`,
@@ -8231,9 +8239,28 @@ async function intelligenceRoutes(app) {
8231
8239
  if (request.query.runId) {
8232
8240
  conditions.push(eq11(insights.runId, request.query.runId));
8233
8241
  }
8242
+ const typeFilter = request.query.type?.trim();
8243
+ if (typeFilter) {
8244
+ conditions.push(
8245
+ typeFilter.endsWith("*") ? like(insights.type, `${typeFilter.slice(0, -1)}%`) : eq11(insights.type, typeFilter)
8246
+ );
8247
+ }
8248
+ const severityFilter = request.query.severity?.trim();
8249
+ if (severityFilter) {
8250
+ conditions.push(inArray4(insights.severity, severitiesAtOrAbove(severityFilter)));
8251
+ }
8252
+ let limit;
8253
+ if (request.query.limit !== void 0) {
8254
+ const parsed = Number(request.query.limit);
8255
+ if (!Number.isInteger(parsed) || parsed < 1) {
8256
+ throw validationError(`Invalid limit "${request.query.limit}". Use a positive integer.`);
8257
+ }
8258
+ limit = parsed;
8259
+ }
8234
8260
  const rows = app.db.select().from(insights).where(conditions.length === 1 ? conditions[0] : and6(...conditions)).orderBy(desc4(insights.createdAt)).all();
8235
8261
  const showDismissed = request.query.dismissed === "true";
8236
- const result = rows.filter((r) => showDismissed || !r.dismissed).map(mapInsightRow);
8262
+ let result = rows.filter((r) => showDismissed || !r.dismissed).map(mapInsightRow);
8263
+ if (limit !== void 0) result = result.slice(0, limit);
8237
8264
  return reply.send(result);
8238
8265
  });
8239
8266
  app.get("/projects/:name/insights/:id", async (request, reply) => {
@@ -13050,7 +13077,7 @@ async function visibilityStatsRoutes(app) {
13050
13077
  }
13051
13078
 
13052
13079
  // ../api-routes/src/composites.ts
13053
- import { eq as eq17, and as and12, desc as desc9, sql as sql7, like, or as or4, inArray as inArray9 } from "drizzle-orm";
13080
+ import { eq as eq17, and as and12, desc as desc9, sql as sql7, like as like2, or as or4, inArray as inArray9 } from "drizzle-orm";
13054
13081
  var TOP_INSIGHT_LIMIT = 5;
13055
13082
  var SEARCH_HIT_HARD_LIMIT = 50;
13056
13083
  var SEARCH_SNIPPET_RADIUS = 80;
@@ -13230,7 +13257,7 @@ async function compositeRoutes(app) {
13230
13257
  sql7`${querySnapshots.answerText} LIKE ${pattern} ESCAPE '\\'`,
13231
13258
  sql7`${querySnapshots.citedDomains} LIKE ${pattern} ESCAPE '\\'`,
13232
13259
  sql7`${querySnapshots.rawResponse} LIKE ${pattern} ESCAPE '\\'`,
13233
- like(queries.query, pattern)
13260
+ like2(queries.query, pattern)
13234
13261
  )
13235
13262
  )
13236
13263
  ).orderBy(desc9(querySnapshots.createdAt)).limit(limit + 1).all());
@@ -13238,8 +13265,8 @@ async function compositeRoutes(app) {
13238
13265
  and12(
13239
13266
  eq17(insights.projectId, project.id),
13240
13267
  or4(
13241
- like(insights.title, pattern),
13242
- like(insights.query, pattern),
13268
+ like2(insights.title, pattern),
13269
+ like2(insights.query, pattern),
13243
13270
  sql7`${insights.recommendation} LIKE ${pattern} ESCAPE '\\'`,
13244
13271
  sql7`${insights.cause} LIKE ${pattern} ESCAPE '\\'`
13245
13272
  )
@@ -16765,7 +16792,10 @@ var routeCatalog = [
16765
16792
  parameters: [
16766
16793
  nameParameter,
16767
16794
  { name: "dismissed", in: "query", description: "Include dismissed insights (true/false).", schema: stringSchema },
16768
- { name: "runId", in: "query", description: "Filter by run ID.", schema: stringSchema }
16795
+ { name: "runId", in: "query", description: "Filter by run ID.", schema: stringSchema },
16796
+ { name: "type", in: "query", description: "Filter by insight type. Exact match, or a trailing `*` for a prefix (e.g. `gbp-*`).", schema: stringSchema },
16797
+ { name: "severity", in: "query", description: "Minimum severity (low|medium|high|critical); e.g. `high` returns high + critical.", schema: stringSchema },
16798
+ { name: "limit", in: "query", description: "Cap the number of (newest-first) insights returned.", schema: stringSchema }
16769
16799
  ],
16770
16800
  responses: {
16771
16801
  // TODO: Add `InsightDto` Zod schema in contracts.
@@ -20147,11 +20177,12 @@ function normalizeAttribute(raw) {
20147
20177
  if (typeof v === "boolean" || typeof v === "string") values.push(v);
20148
20178
  }
20149
20179
  for (const s of raw.repeatedEnumValue?.setValues ?? []) values.push(s);
20180
+ const unsetValues = [...raw.repeatedEnumValue?.unsetValues ?? []];
20150
20181
  const uris = [];
20151
20182
  for (const u of raw.uriValues ?? []) {
20152
- if (u?.uri) uris.push(u.uri);
20183
+ if (u.uri) uris.push(u.uri);
20153
20184
  }
20154
- return { name: raw.name ?? "", valueType: raw.valueType ?? "", values, uris };
20185
+ return { name: raw.name ?? "", valueType: raw.valueType ?? "", values, unsetValues, uris };
20155
20186
  }
20156
20187
  function countAttributes(attrs) {
20157
20188
  return attrs.length;
@@ -21390,7 +21421,10 @@ async function googleRoutes(app, opts) {
21390
21421
  locationName: r.locationName,
21391
21422
  attributeCount: r.attributeCount,
21392
21423
  syncedAt: r.syncedAt,
21393
- attributes: r.attributes
21424
+ attributes: r.attributes.map((attr) => ({
21425
+ ...attr,
21426
+ unsetValues: attr.unsetValues ?? []
21427
+ }))
21394
21428
  }));
21395
21429
  return { attributes, total: attributes.length };
21396
21430
  });
@@ -34530,8 +34564,8 @@ var IntelligenceService = class {
34530
34564
  *
34531
34565
  * Point-in-time (no previous-run comparison): for each SELECTED location it
34532
34566
  * derives signals from the shared GBP summary math (metric week-over-week,
34533
- * lodging completeness, place-action CTAs) plus the accumulating keyword
34534
- * monthly series (month-over-month keyword drop), then runs the pure
34567
+ * Lodging API readable-group state, place-action CTAs) plus the accumulating
34568
+ * keyword monthly series (month-over-month keyword drop), then runs the pure
34535
34569
  * `analyzeGbp` analyzer. Persists insights ONLY — no health snapshot, which is
34536
34570
  * an answer-visibility concept. Idempotent per `runId`; dismissals are
34537
34571
  * preserved across re-analysis of the same run by the stable insight id.
@@ -995,7 +995,11 @@ var gbpPlaceActionListResponseSchema = z8.object({
995
995
  });
996
996
  var gbpLodgingDtoSchema = z8.object({
997
997
  locationName: z8.string(),
998
- /** Count of non-empty top-level attribute groups (0 = empty profile / AEO gap). */
998
+ /**
999
+ * Count of non-empty top-level groups in the Lodging API response.
1000
+ * 0 means the API returned no readable structured groups; it is a
1001
+ * verify-the-Hotel-details-panel signal, not proof the hotel has no amenities.
1002
+ */
999
1003
  populatedGroupCount: z8.number().int().nonnegative(),
1000
1004
  syncedAt: z8.string(),
1001
1005
  /** Raw Lodging resource as Google returned it. */
@@ -1009,6 +1013,7 @@ var gbpAttributeDtoSchema = z8.object({
1009
1013
  name: z8.string(),
1010
1014
  valueType: z8.string(),
1011
1015
  values: z8.array(z8.union([z8.boolean(), z8.string()])),
1016
+ unsetValues: z8.array(z8.string()),
1012
1017
  uris: z8.array(z8.string())
1013
1018
  });
1014
1019
  var gbpAttributesDtoSchema = z8.object({
@@ -23,7 +23,7 @@ import {
23
23
  trafficConnectVercelRequestSchema,
24
24
  trafficConnectWordpressRequestSchema,
25
25
  trafficEventKindSchema
26
- } from "./chunk-AUQNKEHV.js";
26
+ } from "./chunk-BUGJOQNW.js";
27
27
 
28
28
  // src/config.ts
29
29
  import fs from "fs";
@@ -5024,7 +5024,10 @@ var ApiClient = class {
5024
5024
  path: { name: project },
5025
5025
  query: {
5026
5026
  dismissed: opts?.dismissed ? "true" : void 0,
5027
- runId: opts?.runId
5027
+ runId: opts?.runId,
5028
+ type: opts?.type,
5029
+ severity: opts?.severity,
5030
+ limit: opts?.limit !== void 0 ? String(opts.limit) : void 0
5028
5031
  }
5029
5032
  })
5030
5033
  );
@@ -6312,7 +6315,7 @@ var canonryMcpTools = [
6312
6315
  defineTool({
6313
6316
  name: "canonry_gbp_lodging",
6314
6317
  title: "Get GBP lodging attributes",
6315
- description: 'List the latest Google Business Profile lodging snapshot per location (hotel structured attributes). populatedGroupCount=0 means the Lodging API returned no structured attributes, which is common even for complete hotels because the owner-set "Hotel details" amenity panel is a separate surface the API does not expose. Treat it as a "verify the Hotel details panel", not a confirmed gap.',
6318
+ description: 'List the latest Google Business Profile lodging snapshot per location (hotel structured attributes). populatedGroupCount=0 means the Lodging API returned no readable structured groups, which can happen even for complete hotels whose owner-facing "Hotel details" panel has amenities set. Treat it as a "verify the Hotel details panel", not a confirmed gap.',
6316
6319
  access: "read",
6317
6320
  tier: "gbp",
6318
6321
  inputSchema: gbpLocationScopedInputSchema,
@@ -6345,7 +6348,7 @@ var canonryMcpTools = [
6345
6348
  defineTool({
6346
6349
  name: "canonry_gbp_summary",
6347
6350
  title: "Get GBP local-AEO summary",
6348
- description: "Composite Google Business Profile summary for a project: performance totals + 7d deltas, keyword thresholded %, place-action CTA presence, lodging coverage, and owner-content profile completeness (how many selected locations populate secondary categories / description / service area / hours / phone, plus closed-status counts). All derived numbers computed server-side.",
6351
+ description: "Composite Google Business Profile summary for a project: performance totals + 7d deltas, keyword thresholded %, place-action CTA presence, Lodging API readable-group counts, and owner-content profile completeness (how many selected locations populate secondary categories / description / service area / hours / phone, plus closed-status counts). All derived numbers computed server-side.",
6349
6352
  access: "read",
6350
6353
  tier: "gbp",
6351
6354
  inputSchema: gbpLocationScopedInputSchema,
package/dist/cli.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  setTelemetrySource,
28
28
  showFirstRunNotice,
29
29
  trackEvent
30
- } from "./chunk-ADTLQHJY.js";
30
+ } from "./chunk-4E4JPS37.js";
31
31
  import {
32
32
  CliError,
33
33
  EXIT_SYSTEM_ERROR,
@@ -44,7 +44,7 @@ import {
44
44
  saveConfig,
45
45
  saveConfigPatch,
46
46
  usageError
47
- } from "./chunk-PK46TKGM.js";
47
+ } from "./chunk-FFSJRZJR.js";
48
48
  import {
49
49
  apiKeys,
50
50
  createClient,
@@ -52,7 +52,7 @@ import {
52
52
  projects,
53
53
  queries,
54
54
  renderReportHtml
55
- } from "./chunk-KBDQ5OE4.js";
55
+ } from "./chunk-7YU6ILWL.js";
56
56
  import {
57
57
  BacklinkSources,
58
58
  CcReleaseSyncStatuses,
@@ -77,7 +77,7 @@ import {
77
77
  providerQuotaPolicySchema,
78
78
  resolveProviderInput,
79
79
  winnabilityClassSchema
80
- } from "./chunk-AUQNKEHV.js";
80
+ } from "./chunk-BUGJOQNW.js";
81
81
 
82
82
  // src/cli.ts
83
83
  import { pathToFileURL } from "url";
@@ -3534,7 +3534,11 @@ async function gbpAttributes(project, opts) {
3534
3534
  console.log(` ${a.locationName} ${a.attributeCount} attribute(s)`);
3535
3535
  for (const attr of a.attributes) {
3536
3536
  const key = attr.name.replace(/^attributes\//, "");
3537
- const val = attr.uris.length > 0 ? attr.uris.join(", ") : attr.values.join(", ");
3537
+ const val = [
3538
+ ...attr.values.map(String),
3539
+ ...attr.unsetValues.map((v) => `unset:${v}`),
3540
+ ...attr.uris
3541
+ ].join(", ");
3538
3542
  console.log(` ${key}: ${val}`);
3539
3543
  }
3540
3544
  }
@@ -3586,7 +3590,7 @@ async function gbpSummary(project, opts) {
3586
3590
  console.log(`
3587
3591
  Keywords: ${s.keywords.total} tracked, ${s.keywords.thresholdedPct}% privacy-thresholded`);
3588
3592
  console.log(`Place actions: ${s.placeActions.total} CTA(s) \u2014 reservation:${s.placeActions.hasReservationCta ? "yes" : "no"} booking:${s.placeActions.hasBookingCta ? "yes" : "no"} direct-merchant:${s.placeActions.hasDirectMerchantCta ? "yes" : "no"}`);
3589
- console.log(`Lodging: ${s.lodging.lodgingLocationCount} profile(s), ${s.lodging.populatedLodgingCount} populated, ${s.lodging.emptyLodgingCount} empty`);
3593
+ console.log(`Lodging API: ${s.lodging.lodgingLocationCount} profile(s), ${s.lodging.populatedLodgingCount} with readable groups, ${s.lodging.emptyLodgingCount} with 0 readable groups`);
3590
3594
  const pc = s.profileCompleteness;
3591
3595
  console.log(`Profile completeness (${pc.locationCount} location(s)): ${pc.withSecondaryCategories} w/ secondary categories (${pc.secondaryCategoryTotal} total), ${pc.withDescription} w/ description, ${pc.withServiceArea} w/ service area, ${pc.withHours} w/ hours, ${pc.withPrimaryPhone} w/ phone` + (pc.permanentlyClosed || pc.temporarilyClosed ? `; closed: ${pc.permanentlyClosed} permanently, ${pc.temporarilyClosed} temporarily` : ""));
3592
3596
  }
@@ -9294,7 +9298,13 @@ var SNAPSHOT_CLI_COMMANDS = [
9294
9298
  // src/commands/insights.ts
9295
9299
  async function listInsights(project, opts) {
9296
9300
  const client = createApiClient();
9297
- const insights = await client.getInsights(project, { dismissed: opts.dismissed, runId: opts.runId });
9301
+ const insights = await client.getInsights(project, {
9302
+ dismissed: opts.dismissed,
9303
+ runId: opts.runId,
9304
+ type: opts.type,
9305
+ severity: opts.severity,
9306
+ limit: opts.limit
9307
+ });
9298
9308
  if (opts.format === "json") {
9299
9309
  console.log(JSON.stringify(insights, null, 2));
9300
9310
  return;
@@ -9699,17 +9709,23 @@ function printGaps2(data) {
9699
9709
  var INTELLIGENCE_CLI_COMMANDS = [
9700
9710
  {
9701
9711
  path: ["insights"],
9702
- usage: "canonry insights <project> [--dismissed] [--run-id <id>] [--format json]",
9712
+ usage: "canonry insights <project> [--type <type|prefix*>] [--severity <low|medium|high|critical>] [--limit <n>] [--dismissed] [--run-id <id>] [--format json]",
9703
9713
  options: {
9704
9714
  dismissed: { type: "boolean" },
9705
- "run-id": { type: "string" }
9715
+ "run-id": { type: "string" },
9716
+ type: { type: "string" },
9717
+ severity: { type: "string" },
9718
+ limit: { type: "string" }
9706
9719
  },
9707
9720
  run: async (input) => {
9708
- const usage = "canonry insights <project> [--dismissed] [--run-id <id>] [--format json]";
9721
+ const usage = "canonry insights <project> [--type <type|prefix*>] [--severity <low|medium|high|critical>] [--limit <n>] [--dismissed] [--run-id <id>] [--format json]";
9709
9722
  const project = requireProject(input, "insights", usage);
9710
9723
  const dismissed = input.values.dismissed === true;
9711
9724
  const runId = getString(input.values, "run-id");
9712
- await listInsights(project, { dismissed, runId, format: input.format });
9725
+ const type = getString(input.values, "type");
9726
+ const severity = getString(input.values, "severity");
9727
+ const limit = parseIntegerOption(input, "limit", { message: "limit must be an integer", usage, command: "insights" });
9728
+ await listInsights(project, { dismissed, runId, type, severity, limit, format: input.format });
9713
9729
  }
9714
9730
  },
9715
9731
  {
package/dist/index.d.ts CHANGED
@@ -157,7 +157,15 @@ interface VercelTrafficConfigEntry {
157
157
  connections?: VercelTrafficConnectionConfigEntry[];
158
158
  }
159
159
  interface AgentConfigEntry {
160
- /** Agent mode. Only 'disabled' is valid until the native loop ships. */
160
+ /**
161
+ * Agent mode. `'disabled'` turns the built-in Aero agent OFF entirely — the
162
+ * proactive auto-wake on run completion does not fire, the `SessionRegistry`
163
+ * is not constructed, and the interactive agent routes (`/projects/:name/
164
+ * agent/*`) plus the `canonry agent ask` CLI (a thin client of those routes)
165
+ * are not served. Absent (the default) leaves Aero enabled. Resolved, with
166
+ * the `CANONRY_AGENT_DISABLED` env override, by `resolveAgentEnabled` in
167
+ * agent-config.ts.
168
+ */
161
169
  mode?: 'disabled';
162
170
  }
163
171
  /**
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createServer
3
- } from "./chunk-ADTLQHJY.js";
3
+ } from "./chunk-4E4JPS37.js";
4
4
  import {
5
5
  loadConfig
6
- } from "./chunk-PK46TKGM.js";
7
- import "./chunk-KBDQ5OE4.js";
8
- import "./chunk-AUQNKEHV.js";
6
+ } from "./chunk-FFSJRZJR.js";
7
+ import "./chunk-7YU6ILWL.js";
8
+ import "./chunk-BUGJOQNW.js";
9
9
  export {
10
10
  createServer,
11
11
  loadConfig
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  IntelligenceService
3
- } from "./chunk-KBDQ5OE4.js";
4
- import "./chunk-AUQNKEHV.js";
3
+ } from "./chunk-7YU6ILWL.js";
4
+ import "./chunk-BUGJOQNW.js";
5
5
  export {
6
6
  IntelligenceService
7
7
  };
package/dist/mcp.js CHANGED
@@ -3,10 +3,10 @@ import {
3
3
  PACKAGE_VERSION,
4
4
  canonryMcpTools,
5
5
  createApiClient
6
- } from "./chunk-PK46TKGM.js";
6
+ } from "./chunk-FFSJRZJR.js";
7
7
  import {
8
8
  isReadOnlyKey
9
- } from "./chunk-AUQNKEHV.js";
9
+ } from "./chunk-BUGJOQNW.js";
10
10
 
11
11
  // src/mcp/cli.ts
12
12
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainyc/canonry",
3
- "version": "4.93.0",
3
+ "version": "4.95.0",
4
4
  "type": "module",
5
5
  "description": "Agent-first open-source AEO operating platform - track how answer engines cite your domain",
6
6
  "license": "FSL-1.1-ALv2",
@@ -62,26 +62,26 @@
62
62
  "@types/node-cron": "^3.0.11",
63
63
  "tsup": "^8.5.1",
64
64
  "tsx": "^4.19.0",
65
- "@ainyc/canonry-api-client": "0.0.0",
66
65
  "@ainyc/canonry-api-routes": "0.0.0",
67
66
  "@ainyc/canonry-contracts": "0.0.0",
68
- "@ainyc/canonry-config": "0.0.0",
69
- "@ainyc/canonry-integration-bing": "0.0.0",
70
67
  "@ainyc/canonry-integration-openai-ads": "0.0.0",
71
- "@ainyc/canonry-integration-commoncrawl": "0.0.0",
68
+ "@ainyc/canonry-integration-bing": "0.0.0",
69
+ "@ainyc/canonry-api-client": "0.0.0",
72
70
  "@ainyc/canonry-db": "0.0.0",
73
- "@ainyc/canonry-integration-google": "0.0.0",
74
71
  "@ainyc/canonry-integration-cloud-run": "0.0.0",
72
+ "@ainyc/canonry-integration-commoncrawl": "0.0.0",
73
+ "@ainyc/canonry-config": "0.0.0",
74
+ "@ainyc/canonry-integration-google": "0.0.0",
75
75
  "@ainyc/canonry-integration-google-business-profile": "0.0.0",
76
76
  "@ainyc/canonry-integration-google-places": "0.0.0",
77
77
  "@ainyc/canonry-integration-wordpress": "0.0.0",
78
78
  "@ainyc/canonry-integration-traffic": "0.0.0",
79
- "@ainyc/canonry-provider-cdp": "0.0.0",
80
79
  "@ainyc/canonry-intelligence": "0.0.0",
81
- "@ainyc/canonry-provider-local": "0.0.0",
80
+ "@ainyc/canonry-provider-cdp": "0.0.0",
81
+ "@ainyc/canonry-provider-claude": "0.0.0",
82
82
  "@ainyc/canonry-provider-gemini": "0.0.0",
83
+ "@ainyc/canonry-provider-local": "0.0.0",
83
84
  "@ainyc/canonry-provider-openai": "0.0.0",
84
- "@ainyc/canonry-provider-claude": "0.0.0",
85
85
  "@ainyc/canonry-provider-perplexity": "0.0.0"
86
86
  },
87
87
  "scripts": {