@canonry/canonry 7.98.0 → 7.100.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 (182) hide show
  1. package/README.md +18 -16
  2. package/assets/agent-workspace/AGENTS.md +13 -6
  3. package/assets/agent-workspace/skills/aero/SKILL.md +45 -26
  4. package/assets/agent-workspace/skills/aero/references/agent-operations.md +47 -12
  5. package/assets/agent-workspace/skills/aero/references/memory-patterns.md +1 -1
  6. package/assets/agent-workspace/skills/aero/references/orchestration.md +9 -9
  7. package/assets/agent-workspace/skills/aero/references/reporting.md +8 -4
  8. package/assets/agent-workspace/skills/aero/references/seo-analysis.md +2 -2
  9. package/assets/agent-workspace/skills/aero/references/site-health.md +34 -23
  10. package/assets/agent-workspace/skills/aero/soul.md +2 -2
  11. package/assets/agent-workspace/skills/canonry/SKILL.md +37 -7
  12. package/assets/agent-workspace/skills/canonry/references/aeo-analysis.md +14 -11
  13. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +122 -53
  14. package/assets/assets/AccessControls-DzO5Y8v9.js +2 -0
  15. package/assets/assets/AgentSetupPage-DWWovaCw.js +36 -0
  16. package/assets/assets/AuditHistoryPanel-BG-n1i8c.js +1 -0
  17. package/assets/assets/BacklinksPage-D6wdOMXt.js +1 -0
  18. package/assets/assets/ChartPrimitives-CtKdkn64.js +3 -0
  19. package/assets/assets/Hint-3TTi0MgX.js +1 -0
  20. package/assets/assets/{HistoryPage-DeN79d3c.js → HistoryPage-B_ReWHbL.js} +1 -1
  21. package/assets/assets/InfoTooltip-D29JcNMW.js +1 -0
  22. package/assets/assets/MeasurementPropertyPage-zdyLL-kl.js +4 -0
  23. package/assets/assets/ProjectPage-KZNKXR4w.js +15 -0
  24. package/assets/assets/RunsPage-DxN0xXQh.js +1 -0
  25. package/assets/assets/ServerTrafficSection-CvISj5pf.js +40 -0
  26. package/assets/assets/SettingsPage-CpRpBlMV.js +1 -0
  27. package/assets/assets/ToneBadge-DcwPruHr.js +60 -0
  28. package/assets/assets/TrafficPage-zk6zMZyz.js +1 -0
  29. package/assets/assets/TrafficSourceDetailPage-DJtsI06Z.js +1 -0
  30. package/assets/assets/{account-context-CdEHcI5S.js → account-context-CoJVqEXZ.js} +1 -1
  31. package/assets/assets/{agent-trust-copy-N6WacCSg.js → agent-trust-copy-BIXDMtBm.js} +1 -1
  32. package/assets/assets/{card-B1O0SunG.js → card-Dwmc7Srx.js} +1 -1
  33. package/assets/assets/index-fh1T5JQY.css +1 -0
  34. package/assets/assets/index-mqc2mWUR.js +42 -0
  35. package/assets/assets/query-client-Tg1fZu4i.js +1 -0
  36. package/assets/assets/react-query-BqcEEfEO.js +1 -0
  37. package/assets/assets/{react-sigma_core.esm.min-f4uuOgrw.js → react-sigma_core.esm.min-D1q0DR_j.js} +3 -3
  38. package/assets/assets/{server-traffic-CkSCFMnL.js → server-traffic-Djnh_9f0.js} +1 -1
  39. package/assets/assets/service-BQjBdN4O.js +1 -0
  40. package/assets/assets/use-tooltip-position-BOzm-6Wy.js +1 -0
  41. package/assets/assets/vendor-lucide-iE5MEh2-.js +1 -0
  42. package/assets/assets/{vendor-markdown-BDQ1GKGP.js → vendor-markdown-BBGbNklj.js} +1 -1
  43. package/assets/assets/{vendor-radix-BGYlvS2l.js → vendor-radix-DKCt5Z_h.js} +1 -1
  44. package/assets/assets/{vendor-recharts-DhEHcK3G.js → vendor-recharts-D3MLFU3q.js} +1 -1
  45. package/assets/assets/{vendor-tanstack-BNuZ37rE.js → vendor-tanstack-B90HW50P.js} +2 -2
  46. package/assets/index.html +16 -15
  47. package/dist/{ads-PQ4OP6OH.js → ads-FVPZ6R6Z.js} +3 -3
  48. package/dist/{agent-B6ZPL2HH.js → agent-2LPU2E5I.js} +7 -7
  49. package/dist/{agent-ask-ZUKNXRAX.js → agent-ask-5ODREK6D.js} +3 -3
  50. package/dist/{agent-conversations-DJ7WU7PL.js → agent-conversations-SNLOOJEM.js} +3 -3
  51. package/dist/{agent-journeys-LGUI3SQG.js → agent-journeys-XDXVL6PK.js} +3 -3
  52. package/dist/{agent-memory-PWZJ7G5Q.js → agent-memory-NKAI2S3U.js} +3 -3
  53. package/dist/{agent-providers-TEERDNJT.js → agent-providers-R5L7A72X.js} +3 -3
  54. package/dist/{agent-transcript-KDPOPK5Z.js → agent-transcript-C5WAGYVC.js} +3 -3
  55. package/dist/{analytics-NFW5RQRT.js → analytics-EJVOLIQL.js} +33 -13
  56. package/dist/{apply-EP4V4UF5.js → apply-I6JOM247.js} +3 -3
  57. package/dist/{backfill-6UTNNVFO.js → backfill-UVUR36IB.js} +10 -10
  58. package/dist/{backlinks-SG5GV5X5.js → backlinks-3VMVACWN.js} +3 -3
  59. package/dist/{bing-Q4W24JNC.js → bing-R5RL3PW2.js} +3 -3
  60. package/dist/{bootstrap-WEMERVNZ.js → bootstrap-GMMSDTNO.js} +12 -12
  61. package/dist/{cdp-3XKBJGHO.js → cdp-FJKUXHEY.js} +7 -7
  62. package/dist/{chunk-KK3Z34AN.js → chunk-2DV7F6XA.js} +91 -2
  63. package/dist/{chunk-MTW37GXF.js → chunk-2FWYWGIR.js} +2 -2
  64. package/dist/{chunk-Q5SNOMRB.js → chunk-56VVRWJM.js} +14 -5
  65. package/dist/{chunk-52M2JWCB.js → chunk-7BEGCVVB.js} +1 -1
  66. package/dist/{chunk-WZSDGC45.js → chunk-7BH53MOB.js} +27 -2
  67. package/dist/{chunk-IX5AC3QB.js → chunk-7J2RIIYU.js} +1450 -1067
  68. package/dist/{chunk-UXXZXXNG.js → chunk-7SNMIXEQ.js} +2 -2
  69. package/dist/{chunk-2XCYEBQV.js → chunk-AMGM55CP.js} +2 -2
  70. package/dist/{chunk-MSI4BGFN.js → chunk-AYKTVQF7.js} +13 -5
  71. package/dist/{chunk-3JWY65XO.js → chunk-BKDRT4VS.js} +7 -7
  72. package/dist/{chunk-FQ3L2TIX.js → chunk-COHSL6KW.js} +1 -1
  73. package/dist/{chunk-X3UXMHWC.js → chunk-DGDLLRS4.js} +1 -1
  74. package/dist/{chunk-YJITDEZR.js → chunk-DHIB7EIA.js} +4 -4
  75. package/dist/{chunk-BLW327AU.js → chunk-EE3QTCLS.js} +18 -6
  76. package/dist/{chunk-3GMBHGQ5.js → chunk-F46TBLXK.js} +204 -94
  77. package/dist/{chunk-RTH5JVC3.js → chunk-FBO25TA3.js} +1 -1
  78. package/dist/{chunk-2Q7JEC6N.js → chunk-FZ3MUTU3.js} +2 -2
  79. package/dist/{chunk-64R66MWZ.js → chunk-G6QN4VFS.js} +16 -10
  80. package/dist/{chunk-ISIJ2NS4.js → chunk-HBHLYWKE.js} +9 -6
  81. package/dist/{chunk-EJZ444VV.js → chunk-HL2LGRTI.js} +8 -6
  82. package/dist/chunk-J6536OL6.js +31 -0
  83. package/dist/{chunk-RLKI2QF7.js → chunk-KVRX7YIH.js} +20 -10
  84. package/dist/{chunk-3BA75QYU.js → chunk-LJO2BKWU.js} +1 -1
  85. package/dist/{chunk-CN6MVUY3.js → chunk-LNVFERI4.js} +1 -1
  86. package/dist/{chunk-ZADRESLB.js → chunk-NQYJHKML.js} +39 -7
  87. package/dist/{chunk-IAGC2MGP.js → chunk-O5UFAXUI.js} +646 -588
  88. package/dist/{chunk-RGFIRJ7F.js → chunk-O6APBEJE.js} +2 -2
  89. package/dist/{chunk-ZBTUISPG.js → chunk-O7S7OHLE.js} +9399 -8388
  90. package/dist/{chunk-ER5ZHAB2.js → chunk-QS5DWJDI.js} +2 -2
  91. package/dist/{chunk-QSL3FXE4.js → chunk-RBG2QXQM.js} +4 -4
  92. package/dist/{chunk-SAKH2RRF.js → chunk-SWZXHHAG.js} +16 -8
  93. package/dist/{chunk-VMGZBH5L.js → chunk-T7UDY7W3.js} +26 -11
  94. package/dist/{chunk-OL2L5ANJ.js → chunk-UGO2FO6V.js} +119 -55
  95. package/dist/{chunk-J6MYB3F4.js → chunk-VHULVUDL.js} +1 -1
  96. package/dist/{chunk-O22WSFWL.js → chunk-YGAQQSCL.js} +56339 -54966
  97. package/dist/{chunk-TNOYUE2L.js → chunk-YIIRS7V7.js} +98 -65
  98. package/dist/{chunk-64KB6YTG.js → chunk-YU2WX7RD.js} +3 -3
  99. package/dist/{citations-5OSQ3ZYU.js → citations-LOEQRDUB.js} +21 -10
  100. package/dist/{cli-catalog-T5OAVKGS.js → cli-catalog-5RXNUFBS.js} +3 -3
  101. package/dist/{cli-commands-VATCVJKW.js → cli-commands-HE2BQY3Y.js} +7 -7
  102. package/dist/cli.js +22 -17
  103. package/dist/{competitor-YIKTMTHY.js → competitor-PIX2BHJW.js} +3 -3
  104. package/dist/{content-DS2LKR7J.js → content-PJGW4WZF.js} +3 -3
  105. package/dist/{dashboard-HLVRRRI4.js → dashboard-7OKBD4MG.js} +6 -6
  106. package/dist/{demo-A5DTVJGK.js → demo-SCOFAWFM.js} +1 -1
  107. package/dist/{demo-server-P2JGAI6C.js → demo-server-SJPI6RSL.js} +38 -36
  108. package/dist/{discover-VNZDO2MU.js → discover-I3WL2QUN.js} +10 -8
  109. package/dist/{discover-eval-ON77LUEN.js → discover-eval-YRECOIP5.js} +3 -3
  110. package/dist/{doctor-7TNNTI7H.js → doctor-F4J6LILX.js} +3 -3
  111. package/dist/{evidence-E54CNGXS.js → evidence-MYQTCY4V.js} +25 -10
  112. package/dist/{export-cmd-XXZ6KLQQ.js → export-cmd-3SUFOJM6.js} +3 -3
  113. package/dist/{ga-ZQB4EJI3.js → ga-VA6FYK4L.js} +3 -3
  114. package/dist/{gbp-ZQL6NQEK.js → gbp-DI6ZVFHL.js} +3 -3
  115. package/dist/{google-7EIHQGSV.js → google-UVKWJCD2.js} +4 -4
  116. package/dist/{google-marketing-RSAO4PG7.js → google-marketing-S63HN733.js} +1 -1
  117. package/dist/{health-cmd-R7WPKGWX.js → health-cmd-DOZXGI46.js} +16 -9
  118. package/dist/{history-2CSWSVCY.js → history-UTJTUDYK.js} +3 -3
  119. package/dist/index.d.ts +204 -12
  120. package/dist/index.js +26 -26
  121. package/dist/{init-FXZRMXZ3.js → init-IVJSS33J.js} +19 -19
  122. package/dist/{insights-EJUL3W6D.js → insights-UHCVCZ76.js} +3 -3
  123. package/dist/intelligence-service-3EENJ5DS.js +16 -0
  124. package/dist/{journeys-YUMBANFH.js → journeys-XIT7WDM2.js} +3 -3
  125. package/dist/{keys-KLOTGT4H.js → keys-MQKW4JUI.js} +3 -3
  126. package/dist/{keyword-5NSJFEOI.js → keyword-EASMKYZ3.js} +3 -3
  127. package/dist/{logs-WW3RLCRG.js → logs-ZHZ6XM55.js} +3 -3
  128. package/dist/{mcp-NTBEIEVX.js → mcp-ECVT2YGU.js} +4 -4
  129. package/dist/mcp.js +10 -10
  130. package/dist/{measurement-plan-BLPAZEXS.js → measurement-plan-7FQKUOMR.js} +5 -5
  131. package/dist/{notify-KAGE6UEN.js → notify-TC7ARC6W.js} +3 -3
  132. package/dist/{organic-evidence-HTYRXRML.js → organic-evidence-M34MWJXE.js} +7 -4
  133. package/dist/overview-P6BRMXJN.js +314 -0
  134. package/dist/{project-NYUWICVC.js → project-WZ2YXFPJ.js} +4 -4
  135. package/dist/project-setup-WV6SPGIV.js +176 -0
  136. package/dist/{query-3CRRDOHD.js → query-WAFJKDHG.js} +3 -3
  137. package/dist/{research-ZULVD3TW.js → research-B5ZPQX7R.js} +3 -3
  138. package/dist/{results-export-STM7SQ4J.js → results-export-NIZV6R3K.js} +3 -3
  139. package/dist/{run-S4MFU4MK.js → run-ICLPZ7QT.js} +7 -4
  140. package/dist/{schedule-6W3Q2FUL.js → schedule-4C44LQ7R.js} +22 -3
  141. package/dist/{search-EKXXN7C4.js → search-UJM7KII3.js} +11 -4
  142. package/dist/{serve-TFBFPFAQ.js → serve-A6UIO6WP.js} +30 -29
  143. package/dist/{session-4B5V3AH6.js → session-RTE22GQT.js} +14 -14
  144. package/dist/settings-BOF5Z7ML.js +211 -0
  145. package/dist/{sitemaps-R7XFCH6E.js → sitemaps-BM7S76H2.js} +4 -4
  146. package/dist/{skills-44XYCTVD.js → skills-X5SR4HRO.js} +2 -2
  147. package/dist/{snapshot-ROAOJI4W.js → snapshot-KLKE37VY.js} +4 -4
  148. package/dist/{src-ZJRIKEDB.js → src-EKLG5A6O.js} +4 -2
  149. package/dist/{status-EN3A2TX5.js → status-OYG57DWU.js} +6 -5
  150. package/dist/{technical-aeo-ZV466F65.js → technical-aeo-KWVWOOTI.js} +23 -23
  151. package/dist/{telemetry-EZHPNZ2A.js → telemetry-KGE6FF3Y.js} +4 -4
  152. package/dist/{tools-IH4M7AAG.js → tools-4R33AZQ3.js} +11 -11
  153. package/dist/{traffic-VFLXWB3Z.js → traffic-SVMOKCSS.js} +7 -11
  154. package/dist/{trends-ZXJIX3Z3.js → trends-RGAKGX3L.js} +4 -4
  155. package/dist/{users-J2BYEWOE.js → users-47WKA2TO.js} +3 -3
  156. package/dist/{visibility-stats-AJ7NY2BP.js → visibility-stats-DTGQJPNV.js} +9 -5
  157. package/dist/{wordpress-U6MS35VE.js → wordpress-2CNUX4UE.js} +3 -3
  158. package/package.json +2 -2
  159. package/assets/assets/AgentSetupPage-h1kJhy9q.js +0 -18
  160. package/assets/assets/AuditHistoryPanel-CNUApNiJ.js +0 -1
  161. package/assets/assets/BacklinksPage-CpKmlAek.js +0 -1
  162. package/assets/assets/ChartPrimitives-CvK6CQrj.js +0 -1
  163. package/assets/assets/Hint-c4a4W87X.js +0 -1
  164. package/assets/assets/InfoTooltip-Drnst4ML.js +0 -1
  165. package/assets/assets/MeasurementPropertyPage-CcHnYn81.js +0 -4
  166. package/assets/assets/ProjectPage-Tmuqmust.js +0 -11
  167. package/assets/assets/RunsPage-BD4k3MjX.js +0 -1
  168. package/assets/assets/SettingsPage-BHy30JP5.js +0 -1
  169. package/assets/assets/SiteHealthSection-SYdtuGjq.js +0 -2
  170. package/assets/assets/ToneBadge-Cii5KNtg.js +0 -60
  171. package/assets/assets/TrafficPage-DuxtRqnc.js +0 -40
  172. package/assets/assets/TrafficSourceDetailPage-DiX1bB02.js +0 -1
  173. package/assets/assets/format-helpers-V8Pyc7cR.js +0 -3
  174. package/assets/assets/index-BAxHW-1G.css +0 -1
  175. package/assets/assets/index-BTz-B9hV.js +0 -60
  176. package/assets/assets/query-client-C1ycmW1c.js +0 -1
  177. package/assets/assets/react-query-DI7VfOND.js +0 -1
  178. package/assets/assets/service-DSgXWhNq.js +0 -1
  179. package/assets/assets/vendor-lucide-DuOM7wVi.js +0 -1
  180. package/dist/intelligence-service-TKK7O3Z5.js +0 -16
  181. package/dist/overview-GSJ4GP2T.js +0 -213
  182. package/dist/settings-VRTDPFR3.js +0 -119
@@ -15,7 +15,7 @@ import {
15
15
  formatCanonryPluginUpdateHint,
16
16
  formatCanonryPluginVersionMismatch,
17
17
  skillsClientSchema
18
- } from "./chunk-ZBTUISPG.js";
18
+ } from "./chunk-O7S7OHLE.js";
19
19
 
20
20
  // src/commands/skills.ts
21
21
  import crypto from "crypto";
@@ -5,7 +5,7 @@ import {
5
5
  siteCrawlGraphNodes,
6
6
  siteCrawlPages,
7
7
  siteCrawlSnapshots
8
- } from "./chunk-KK3Z34AN.js";
8
+ } from "./chunk-2DV7F6XA.js";
9
9
  import {
10
10
  SITE_CRAWL_GRAPH_MAX_EDGES,
11
11
  SITE_CRAWL_GRAPH_MAX_NODES,
@@ -15,7 +15,7 @@ import {
15
15
  factorStatusFromDistribution,
16
16
  factorStatusFromScore,
17
17
  roundPreservingTotal
18
- } from "./chunk-ZBTUISPG.js";
18
+ } from "./chunk-O7S7OHLE.js";
19
19
 
20
20
  // src/site-audit-factors.ts
21
21
  function recordedSharePct(factor) {
@@ -3,16 +3,16 @@ import {
3
3
  CANONRY_MCP_TOOLKIT_NAMES,
4
4
  isCanonryMcpToolkitName,
5
5
  resolveAeroSkillDir
6
- } from "./chunk-MTW37GXF.js";
6
+ } from "./chunk-2FWYWGIR.js";
7
7
  import {
8
8
  createApiClient,
9
9
  runWithUsageTags
10
- } from "./chunk-OL2L5ANJ.js";
10
+ } from "./chunk-UGO2FO6V.js";
11
11
  import {
12
12
  canonryMcpTools,
13
13
  mcpOutputSchema,
14
14
  toolOperationAccess
15
- } from "./chunk-3GMBHGQ5.js";
15
+ } from "./chunk-F46TBLXK.js";
16
16
  import {
17
17
  PACKAGE_VERSION
18
18
  } from "./chunk-AOJU5LKX.js";
@@ -25,7 +25,7 @@ import {
25
25
  isReadOnlyKey,
26
26
  restrictedWriteScopes,
27
27
  upgradeCaveatFor
28
- } from "./chunk-ZBTUISPG.js";
28
+ } from "./chunk-O7S7OHLE.js";
29
29
 
30
30
  // src/mcp/server.ts
31
31
  import { randomUUID } from "crypto";
@@ -131,6 +131,9 @@ function zodErrorMessage(error) {
131
131
  }
132
132
 
133
133
  // src/mcp/dynamic-catalog.ts
134
+ function isListed(entry) {
135
+ return entry.tool.deprecatedAliasOf === void 0;
136
+ }
134
137
  var DynamicToolCatalog = class {
135
138
  entries;
136
139
  loaded = /* @__PURE__ */ new Set();
@@ -168,8 +171,9 @@ var DynamicToolCatalog = class {
168
171
  if (matches.length === 0) {
169
172
  return { status: "empty", name, tools: [] };
170
173
  }
174
+ const listed = matches.filter(isListed).map((entry) => entry.tool.name);
171
175
  if (this.loaded.has(name)) {
172
- return { status: "already-loaded", name, tools: matches.map((entry) => entry.tool.name) };
176
+ return { status: "already-loaded", name, tools: listed };
173
177
  }
174
178
  this.batchListChanged(() => {
175
179
  for (const entry of matches) {
@@ -177,14 +181,16 @@ var DynamicToolCatalog = class {
177
181
  }
178
182
  });
179
183
  this.loaded.add(name);
180
- return { status: "loaded", name, tools: matches.map((entry) => entry.tool.name) };
184
+ return { status: "loaded", name, tools: listed };
181
185
  }
182
186
  helpResult() {
183
187
  return {
184
188
  scope: this.scope,
185
189
  eager: this.eager,
186
190
  loadedToolkits: [...this.loaded].sort(),
187
- coreTools: this.entries.filter((entry) => entry.tool.tier === "core").map((entry) => entry.tool.name),
191
+ coreTools: this.entries.filter((entry) => entry.tool.tier === "core" && isListed(entry)).map((entry) => entry.tool.name),
192
+ // An approval list, so it stays complete: a live tool needs approval
193
+ // under any name it answers to.
188
194
  liveTools: this.entries.filter((entry) => entry.tool.tier === "core" && toolOperationAccess(entry.tool) === OperationAccesses.live).map((entry) => entry.tool.name),
189
195
  toolkits: CANONRY_MCP_TOOLKITS.map((toolkit) => this.toolkitEntry(toolkit)).filter((entry) => entry.toolCount > 0),
190
196
  usage: this.eager ? "All tools available on this connection are already loaded. Use their listed schemas; tool visibility does not grant server permissions." : "Call canonry_load_toolkit with one of the toolkit names listed in `toolkits[].name` to register its tools for the rest of this session. Wait for its response before calling any newly enabled tool."
@@ -204,7 +210,7 @@ var DynamicToolCatalog = class {
204
210
  };
205
211
  }
206
212
  toolsForToolkit(name) {
207
- return this.entries.filter((entry) => entry.tool.tier === name).map((entry) => entry.tool.name);
213
+ return this.entries.filter((entry) => entry.tool.tier === name && isListed(entry)).map((entry) => entry.tool.name);
208
214
  }
209
215
  // RegisteredTool.enable/disable each call sendToolListChanged on the McpServer
210
216
  // we registered with. Loading an 11-tool toolkit emits 11 notifications under
@@ -232,7 +238,7 @@ var OPERATIONS_GUIDE = {
232
238
  "guideVersion": "v1",
233
239
  "operationsGuideUrl": "https://github.com/Canonry/canonry/blob/main/docs/agent-operations/v1.md",
234
240
  "resourceUri": "canonry://agent-operations/v1",
235
- "initialize": 'Canonry tracks how AI answer engines mention brands and cite domains. Start with canonry_help({intent:"status"}) or a workflow; follow its stored reads and approval boundaries. Connected MCP clients need no skill, resource, plugin, or local CLI. The Canonry skill offers optional additional guidance.\nmentioned = brand in answer TEXT; cited = domain in SOURCE links. Never compute one from the other. A null answerMentioned means not checked, not false. No recorded sweep means no measured figure.\nGet approval for live provider reads, sweeps, probes, research, syncs and writes, scoped to the exact target, action and work. A GET or readOnlyHint does not mean free. canonry_help returns all liveTools for this connection; each needs approval, including read-only tools.\nREST exposes x-canonry-access; CLI help/catalog expose access; MCP help lists liveTools.\nStored-only: CLI --stored-only or CANONRY_STORED_ONLY=1; MCP --stored-only, --scope=stored-only or CANONRY_MCP_SCOPE=stored-only. It refuses live/write work (including doctor) before execution and suppresses housekeeping. CLI refusal is STORED_ONLY_REFUSED. Explicit exports may write files; server accounting remains separate from business mutations.\nHelp and skills grant no permissions; the server enforces authority. Report missing tools or 403; never bypass them with other credentials or endpoints. Hosted catalogs are fixed; use canonry_load_toolkit only when help offers it.\nReport concrete Canonry failures, confusing output, workarounds or improvements to the user and once via canonry_feedback. Exclude secrets and customer data.\n',
241
+ "initialize": 'Canonry tracks how AI answer engines mention brands and cite domains. Start with canonry_help({intent:"status"}) or a workflow; follow its stored reads and approval boundaries. Connected MCP clients need no skill, resource, plugin, or local CLI. The Canonry skill offers optional additional guidance.\nmentioned = brand in answer TEXT; cited = domain in SOURCE links. Never compute one from the other. A null answerMentioned means not checked, not false. citationState not-measured means the engine returns no sources: never a miss. No recorded sweep means no measured figure.\nGet approval for live provider reads, sweeps, probes, research, syncs and writes, scoped to the exact target, action and work. A GET or readOnlyHint does not mean free. canonry_help returns all liveTools for this connection; each needs approval, including read-only tools.\nREST exposes x-canonry-access; CLI help/catalog expose access; MCP help lists liveTools.\nStored-only: CLI --stored-only or CANONRY_STORED_ONLY=1; MCP --stored-only, --scope=stored-only or CANONRY_MCP_SCOPE=stored-only. It refuses live/write work (including doctor) before execution and suppresses housekeeping. CLI refusal is STORED_ONLY_REFUSED. Explicit exports may write files; server accounting remains separate from business mutations.\nHelp and skills grant no permissions; the server enforces authority. Report missing tools or 403; never bypass them with other credentials or endpoints. Hosted catalogs are fixed; use canonry_load_toolkit only when help offers it.\nReport concrete Canonry failures, confusing output, workarounds or improvements to the user and once via canonry_feedback. Exclude secrets and customer data.\n',
236
242
  "approvalBoundary": [
237
243
  "provider reads",
238
244
  "sweeps",
@@ -400,7 +406,7 @@ var OPERATIONS_GUIDE = {
400
406
  "guidance": "Build the requested summary from stored project overview, organic evidence, and the relevant visibility, measurement, integration, or traffic reads. Retain evidence dates, scope, sample sizes, missing data, and separate mention/citation signals; do not generate fresh runs implicitly."
401
407
  }
402
408
  },
403
- "markdown": "# Canonry Operations Guide v1\n\nCanonry is an agent-first AI visibility platform. MCP is the universal entry\npoint for connected agents. Host-native skills are optional upgrades, not a\nprerequisite or a permission mechanism.\n\n## Connect and choose a route\n\nRead the initialization guidance, then call `canonry_help` with an `intent`:\n`status`, `diagnose`, `operations`, `prospecting`, `measurement`, `integrations`,\n`reports`, or a short task\ndescription. Select an accessible project with `canonry_projects_list` before\nusing its exact name in project tools. Inspect each listed tool's input schema;\nhelp suggests tool names, not invented arguments or authorization.\n\nHelp returns a versioned, compact route: connection `mode`, available `next`\ntools, workflow guidance, approval boundaries, and this guide's URL. It performs\nno provider calls, reads no project data, and changes no permissions.\n`next` lists stored reads. Optional `actions` lists loaded tools for work that\nrequires approval; listing an action does not authorize or execute it.\n`includeCatalog: true` additionally returns toolkit details when needed.\n\nAnalysis routes also return `playbooks`: Canonry's analyst playbooks for that\nwork, the same files its built-in agent reads. Read each one before\ninterpreting results. `canonry_help({ playbook: \"<slug>\" })` returns its full\ntext, a note on using it over MCP, and every available slug. Playbooks are\nguidance, not permission.\n\nHosted connections use a fixed catalog. Help only suggests tools offered by that\nconnection; it never tells a hosted agent to dynamically load a toolkit. A\nprogressive local stdio connection may return `loadToolkits`: call\n`canonry_load_toolkit` with one returned name, await it, then call help again.\nLoading only changes local tool discovery, never server authority.\n\nThe optional `canonry://agent-operations/v1` MCP resource contains this same\nguide. If the host cannot read resources or open links, continue through help.\nDo not install a plugin, local runtime, or skill merely to use connected MCP.\nAn installed Codex or Claude Canonry skill contains a generated copy of this\nguide plus links to host-native references. It does not replace runtime help.\n\n## Vocabulary and evidence\n\n- **Mentioned** means the brand appears in answer text. **Cited** means its\n domain appears in source links. Either, both, or neither can occur; never\n compute one signal from the other.\n- `answerMentioned: null` means not checked, not false. Missing runs and empty\n populations mean no measurement, not zero visibility.\n- Preserve project, time window, provider, requested/served model, location,\n sample size, and query class when comparing evidence. Use server-returned\n metrics; do not invent a score from incompatible populations.\n- Simple projects and Advanced portfolios share the workflow. For Advanced\n results, preserve Property, Target, market, plan revision, and class scope.\n Groups organize navigation; do not infer an unrequested fan-out.\n- Research is isolated evidence, not tracked measurement. A probe still spends\n quota and persists evidence but is excluded from normal tracking metrics.\n\n## Workflows\n\n**Status:** read the stored overview and freshness first. Say when evidence is\nmissing instead of silently creating it.\n\n**Setup:** Page Health needs no answer-engine provider; AI Visibility does.\nAfter a Page Health result on an install with no provider (a `NO_PROVIDER`\nnotice, or doctor's `providers.none-configured`), propose AI Visibility setup\nas the next step. The operator adds the provider key in the dashboard or in\ntheir own terminal; never ask for it in chat.\nRerunning `canonry bootstrap` after a provider change reloads saved provider\nsettings on a matching running local server and reports whether they became\nactive. If the server is offline, start it to apply the saved settings. For an\nexplicit reload of the connected server's own config, use\n`canonry settings reload-providers` or `canonry_providers_reload` with install\nadministrator authority. Remote servers are never reloaded automatically by\nbootstrap, and a local server that belongs to another install is left alone.\nPorts and database settings still need a restart.\n\n**Diagnose:** inspect stored history and comparable evidence. Explain what\nchanged separately from why it might have changed. A hypothesis is not a\nmeasured cause. Propose bounded verification if stored evidence is insufficient.\n\n**Site Health:** read `canonry_site_health_issues` for stored technical findings,\naffected-page counts and example URLs, then `canonry_site_health_issue_pages`\nfor a selected issue's remaining pages. Pin subsequent reads to the returned\nrun. Inspect coverage and omissions: a passing score can still have findings,\nan older scan may lack cross-page evidence, and a partial scan cannot establish\nsite-wide absence. Advisory findings require judgment before proposing a repair.\nAudit evidence informs technical repairs and existing-page recommendations;\nit does not itself establish a new-blog opportunity or authorize any edit.\n\n**Prospecting:** generate a one-shot company snapshot without creating a project.\nInspect stored provider settings first. Agree on the company, domain, selected\nproviders, and queries before starting the quota-spending snapshot action.\nBrowser-only selection requires manual queries. Progressive stdio help offers\nthe discovery toolkit when this connection permits snapshots; load it, then\ncall help again. Fixed catalogs offer only already available actions. Read-only\nand restricted connections must not bypass missing snapshot access.\n\n**Measurement:** inspect the existing setup and results before proposing edits.\nKeep research, query tracking, plan publication, and sweep execution separate.\nFor direct research, submit the final editable query text in one context. For a\nreviewed batch, submit each explicit destination with its final text and one\nidempotency key. Pattern substitution happens in the client before either\nrequest; choosing a market or Property records a destination only and never\nrewrites a query or creates an automatic fan-out. `research.run` does not\nauthorize saving patterns, changing tracking, publishing plans, or settings.\nUse a supported preview where available, inspect its exact destination and\nrevision, then seek approval for the actual change. A preview may itself require\nwrite permission; never treat a dry-run flag as a universal safety guarantee.\n\n**Integrations:** inspect stored connection state and snapshot freshness first.\nProvider configuration evidence does not prove a browser event fired or a\nconversion was recorded. Connection, resource selection, refresh/sync, and live\nreads are separate actions. Credentials belong in the operator's secure setup\nflow, never in chat, tool arguments, reports, or public guidance.\n\n**Reports:** use saved evidence for the requested period and scope. Keep mention\nand citation signals separate, include dates and sample sizes, and state missing\nor stale inputs. For Advanced Property mention rankings, use\n`canonry_measurement_portfolio_summary` and its `mentionRanking.strongest`,\n`.weakest`, and `.excluded` lists. It defaults to non-brand questions; state the\nreturned class and report branded results separately. An unavailable portfolio\naggregate does not invalidate available Property mention rates. Flag excluded\nProperties individually; do not silently replace mention ranking with citation\nranking. Compact `nextCursor` completes only `pageList`; use `list` to enumerate\none ranking, markets, observed names or cited domains, preserving filters.\nFirst-page sibling lists are bounded summaries. Keep sample sizes and ties\nvisible. Preparing a report does not\nauthorize new measurement.\n\n## Authority and approval\n\n### Agent operations\n\nUse `canonry_key_self` (CLI `canonry key whoami --format json`) to inspect the\ncurrent credential's scopes, project boundary, and host-derived `operator`\nauthority without exposing its token. Missing `operator` means unapproved.\n`canonry_settings_get` and\n`canonry_telemetry_get` describe the connected server, not the agent's local\nmachine. Telemetry reports configured preference, effective state, and any\nenvironment override; inspecting status never creates an anonymous identifier.\nAfter approval, `canonry_telemetry_update` changes that preference and\n`canonry_provider_settings_update` changes an already-configured provider's\nmodel/quota. Both require `settings.write`; neither accepts credentials.\nServer telemetry reads and updates additionally require operator authority.\nOrdinary audit-history reads omit internal telemetry events and their state.\n\nOperator authority is deny-by-default and separate from customer admin roles.\nThe deployment owner must approve a dedicated, instance-wide API key's ID in\nthe server environment variable `CANONRY_OPERATOR_KEY_IDS` (comma-separated IDs),\nthen restart the server. Empty/unset approves nobody; wildcards are invalid.\nKeep the bearer private to internal operators; never approve a customer-held or\nshared proxy/bootstrap key. Use `logs.read` for read-only diagnostics, adding\n`settings.write` only when telemetry control is required. Ordinary key creation,\naccount roles, OAuth consent, and caller headers cannot grant operator status.\nRevoking an approved key invalidates it immediately. Host enrollment is a trust\nbootstrap step, intentionally unavailable through customer-facing APIs.\nAPI, CLI, and MCP enforce the same boundary; MCP hides internal tools unless\nthe server confirms operator authority, including in explicit read-only mode.\nProject analytics, research, and normal project permissions are unchanged.\n\n`canonry_logs_list` reads bounded, redacted runtime events from both the\napplication logger and Fastify request/error logging. It requires an\ninstance-wide `logs.read` grant (or wildcard) and a host-approved direct bearer.\nBrowser sessions, OAuth/delegated credentials, customer admins, and project-scoped\nkeys cannot use it, even with a project filter or a matching allowlist ID.\nA `logs.read`-only key is read-only automatically, without a\nsecond `read` marker. Named `*.read` scopes cannot grant mutations; an explicit\nwrite grant is needed and remains subject to its route gates. Returned messages\nare sanitized and bounded; raw request or response bodies, headers, cookies,\nprovider payloads, and stacks are not\npart of the queryable surface. The same secret-redaction policy runs before\nconsole output and storage. Do not deliberately log secrets: redaction is a\ndefense in depth, not permission to put credentials into diagnostic strings.\nOpaque escaped payloads containing secret assignments are omitted when safe\npartial masking cannot be guaranteed; correlate their retained error codes and IDs.\n\nFile-backed hosts retain runtime logs in SQLite across restarts, bounded to\n10,000 events and seven days. In-memory hosts report `retention: \"process\"`.\nFilter by `actor`, `requestId`, `runId`, `projectId`, `module`, `level`, or an\ninclusive `since`/`until` interval. Keep filters unchanged when resuming an\nopaque cursor; retention eviction can invalidate it. Inspect `retentionPolicy`,\n`captureErrors`, `dropped`, `truncated`, and `retention` before drawing\nconclusions. Missing logs are not proof that an action did not happen. Use\n`canonry_project_history` or `canonry_history_global` for persistent audit events;\noffset pages have deterministic ordering but are not snapshots of concurrent writes.\n\nAudit `actor` comes from authenticated identity (`user:<id>` or `api-key:<id>`),\nnot a caller-supplied header. A delegated MCP credential records its originating\nuser as actor and the actual credential in `credentialId`. `requestId` correlates\nHTTP events; `userAgent` and `actorSession` are bounded, untrusted client hints,\nnever identity or permission grants. Older audit rows are not backfilled with\nidentities the server cannot prove.\n\nBoth shipped HTTP hosts issue restart-safe UUID request IDs and return them in\n`x-request-id`. Use that value to correlate a CLI/API failure with log entries;\nHTTP diagnostics retain the method and route template, not raw URL parameters.\nRequest-bound loggers retain completion attribution, while generic background\ncontinuations stop inheriting caller identity after the response completes.\nCapture covers the owning server process after initialization, not arbitrary\nconsole output, other worker processes, or host/container logs. Run one server\ninstance per process and database, as required by the single-tenant deployment\nmodel; this is not a cross-tenant or distributed log collector.\n\nFor CLI use, settings reads are remote. Google setup and telemetry retain their\nlocal defaults: pass `--target server` explicitly to configure the connected\nserver. `schedule list <project>` lists all schedule kinds, and\n`notify events --target server` discovers the server's event catalog.\n\nMCP returns legacy text JSON plus structured results. Objects keep their shape;\narrays use `{items: [...]}` in `structuredContent`, and scalars use `{value: ...}`.\nErrors preserve the existing envelope and CLI exit codes: HTTP 4xx (including\n429 policy limits) use exit 1; HTTP 5xx use exit 2. Server-provided `Retry-After`\nand request IDs are exposed as `retryAfterMs` and `requestId` when available;\nclients do not infer retryability from HTTP 429 or retry automatically.\nFor a write with an ambiguous outcome, inspect saved state or its receipt before\nretrying; a retry hint is not proof that repeating a write is safe.\n\n### Action boundaries\n\nStart with stored evidence. Before a live provider read, sweep, probe, research\nrun, sync, write, or externally visible action, obtain approval covering its\nexact target, action, and bounded work. Approval already given for that exact\noperation need not be asked for again, but does not extend to more projects,\nlarger batches, retries with new identities, or recurring work.\n\nHTTP GET and MCP `readOnlyHint` describe aspects of an operation, not its cost\nor permission. Provider discovery, account reads, and live diagnostics may\nconsume quota even when labeled read-only. If the tool's effect is unclear,\ninspect its description and request direction before calling it.\n\nAuthentication, role/scope checks, project restrictions, quotas, and guarded\napproval receipts are enforced by the server. Help, skills, resources, and tool\nvisibility cannot grant authority. Never change credentials, endpoints, or\nproject identifiers to work around a missing tool or a `403` response.\n\nFor guarded ads writes, inspect unresolved operation receipts before retrying.\nUse the receipt's supported recovery action; do not replay a mutation under a\nnew identity. An executor cannot create or widen its own human approval grant.\nOn ambiguous results, exhausted bounds, or refusal, stop and report what is\nknown and what permission or operator action is needed.\n\n## Version and source\n\nThis public, versioned document is the source for initialization guidance,\nintent routes, the optional resource, and generated Canonry `SKILL.md` files.\nGuide v1 may receive compatible clarifications; incompatible routing contracts\nrequire a new guide version. The running server's help describes its actual\ncatalog and remains usable without fetching this document.\n"
409
+ "markdown": "# Canonry Operations Guide v1\n\nCanonry is an agent-first AI visibility platform. MCP is the universal entry\npoint for connected agents. Host-native skills are optional upgrades, not a\nprerequisite or a permission mechanism.\n\n## Connect and choose a route\n\nRead the initialization guidance, then call `canonry_help` with an `intent`:\n`status`, `diagnose`, `operations`, `prospecting`, `measurement`, `integrations`,\n`reports`, or a short task\ndescription. Select an accessible project with `canonry_projects_list` before\nusing its exact name in project tools. Inspect each listed tool's input schema;\nhelp suggests tool names, not invented arguments or authorization.\n\nHelp returns a versioned, compact route: connection `mode`, available `next`\ntools, workflow guidance, approval boundaries, and this guide's URL. It performs\nno provider calls, reads no project data, and changes no permissions.\n`next` lists stored reads. Optional `actions` lists loaded tools for work that\nrequires approval; listing an action does not authorize or execute it.\n`includeCatalog: true` additionally returns toolkit details when needed.\n\nAnalysis routes also return `playbooks`: Canonry's analyst playbooks for that\nwork, the same files its built-in agent reads. Read each one before\ninterpreting results. `canonry_help({ playbook: \"<slug>\" })` returns its full\ntext, a note on using it over MCP, and every available slug. Playbooks are\nguidance, not permission.\n\nHosted connections use a fixed catalog. Help only suggests tools offered by that\nconnection; it never tells a hosted agent to dynamically load a toolkit. A\nprogressive local stdio connection may return `loadToolkits`: call\n`canonry_load_toolkit` with one returned name, await it, then call help again.\nLoading only changes local tool discovery, never server authority.\n\nThe optional `canonry://agent-operations/v1` MCP resource contains this same\nguide. If the host cannot read resources or open links, continue through help.\nDo not install a plugin, local runtime, or skill merely to use connected MCP.\nAn installed Codex or Claude Canonry skill contains a generated copy of this\nguide plus links to host-native references. It does not replace runtime help.\n\n## Vocabulary and evidence\n\n- **Mentioned** means the brand appears in answer text. **Cited** means its\n domain appears in source links. Either, both, or neither can occur; never\n compute one signal from the other.\n- `answerMentioned: null` means not checked, not false. Missing runs and empty\n populations mean no measurement, not zero visibility.\n- `citationState: not-measured` (with `cited: null`) means the engine returns no\n sources, such as a local model without web access, so whether it cites the\n domain was never observed. It is never a miss: cited rates leave it out and\n count it as `citationNotMeasured`, and a cited rate with nothing measured is\n null, not 0. Mention is still measured on those answers. Other reads show the\n same state in their own shape:\n - visibility report: an answer's `citationUnavailableReason:\n citation-not-measured`, or a rate's `reason: citation-not-measured`;\n - Advanced metrics: `{state: unavailable, reason: citation_not_measured}`;\n - research and snapshot answers: `citationMeasured: false` (research also\n sends `citationState: null`);\n - overview: `citationNotMeasuredQueries`, `citationNotMeasuredPairs` and\n `citationNotMeasuredCount`;\n - analytics: the gaps bucket `citationNotMeasured[]` and sources'\n `citationNotMeasuredAnswers`;\n - visibility compare: `excludedNotMeasured`.\n- Preserve project, time window, provider, requested/served model, location,\n sample size, and query class when comparing evidence. Use server-returned\n metrics; do not invent a score from incompatible populations.\n- Simple projects and Advanced portfolios share the workflow. For Advanced\n results, preserve Property, Target, market, plan revision, and class scope.\n Groups organize navigation; do not infer an unrequested fan-out.\n- Research is isolated evidence, not tracked measurement. A probe still spends\n quota and persists evidence but is excluded from normal tracking metrics.\n\n## Workflows\n\n**Status:** read the stored overview and freshness first. Say when evidence is\nmissing instead of silently creating it.\n\n**Setup:** Agent Readiness needs no answer-engine provider; AI Visibility does.\nAfter an Agent Readiness result on an install with no provider (a `NO_PROVIDER`\nnotice, or doctor's `providers.none-configured`), propose AI Visibility setup\nas the next step. The operator adds the provider key in the dashboard or in\ntheir own terminal; never ask for it in chat. A saved key is not a verified one:\nwith approval, `canonry settings provider test <name>` or `canonry_provider_test`\nchecks the saved connection with the provider now (usually one tiny prompt on\nthe operator's account) and reports `connected`, `failed` with the provider's\nreason, or `not-verified`. It needs install administrator authority and\n`settings.write`; while a connection's test is still running, another test of it\nreturns `OPERATION_IN_PROGRESS`, so wait for that result. The latest result is\nkept as the connection's `lastTest` (`canonry settings provider-info <name>` or\n`canonry_provider_settings_get`) until its key, endpoint, model, Vertex project,\nregion or credentials, or CDP endpoint changes, or the connection is\ndisconnected; read it before testing again.\nRerunning `canonry bootstrap` after a provider change reloads saved provider\nsettings on a matching running local server and reports whether they became\nactive. If the server is offline, start it to apply the saved settings. For an\nexplicit reload of the connected server's own config, use\n`canonry settings reload-providers` or `canonry_providers_reload` with install\nadministrator authority. Remote servers are never reloaded automatically by\nbootstrap, and a local server that belongs to another install is left alone.\nPorts and database settings still need a restart.\n\n**Diagnose:** inspect stored history and comparable evidence. Explain what\nchanged separately from why it might have changed. A hypothesis is not a\nmeasured cause. Propose bounded verification if stored evidence is insufficient.\n\n**Agent Readiness:** read `canonry_agent_readiness_issues` for stored findings,\naffected-page counts and example URLs, then `canonry_agent_readiness_issue_pages`\nfor a selected issue's remaining pages. Pin subsequent reads to the returned\nrun. Inspect coverage and omissions: a passing score can still have findings,\nan older scan may lack cross-page evidence, and a partial scan cannot establish\nsite-wide absence. Advisory findings require judgment before proposing a repair.\nAudit evidence informs technical repairs and existing-page recommendations;\nit does not itself establish a new-blog opportunity or authorize any edit.\n\n**Prospecting:** generate a one-shot company snapshot without creating a project.\nInspect stored provider settings first. Agree on the company, domain, selected\nproviders, and queries before starting the quota-spending snapshot action.\nBrowser-only selection requires manual queries. Progressive stdio help offers\nthe discovery toolkit when this connection permits snapshots; load it, then\ncall help again. Fixed catalogs offer only already available actions. Read-only\nand restricted connections must not bypass missing snapshot access.\n\n**Measurement:** inspect the existing setup and results before proposing edits.\nKeep research, query tracking, plan publication, and sweep execution separate.\nFor direct research, submit the final editable query text in one context. For a\nreviewed batch, submit each explicit destination with its final text and one\nidempotency key. Pattern substitution happens in the client before either\nrequest; choosing a market or Property records a destination only and never\nrewrites a query or creates an automatic fan-out. `research.run` does not\nauthorize saving patterns, changing tracking, publishing plans, or settings.\nUse a supported preview where available, inspect its exact destination and\nrevision, then seek approval for the actual change. A preview may itself require\nwrite permission; never treat a dry-run flag as a universal safety guarantee.\n\n**Integrations:** inspect stored connection state and snapshot freshness first.\nProvider configuration evidence does not prove a browser event fired or a\nconversion was recorded. Connection, resource selection, refresh/sync, and live\nreads are separate actions. Credentials belong in the operator's secure setup\nflow, never in chat, tool arguments, reports, or public guidance.\n\n**Reports:** use saved evidence for the requested period and scope. Keep mention\nand citation signals separate, include dates and sample sizes, and state missing\nor stale inputs. For Advanced Property mention rankings, use\n`canonry_measurement_portfolio_summary` and its `mentionRanking.strongest`,\n`.weakest`, and `.excluded` lists. It defaults to non-brand questions; state the\nreturned class and report branded results separately. An unavailable portfolio\naggregate does not invalidate available Property mention rates. Flag excluded\nProperties individually; do not silently replace mention ranking with citation\nranking. Compact `nextCursor` completes only `pageList`; use `list` to enumerate\none ranking, markets, observed names or cited domains, preserving filters.\nFirst-page sibling lists are bounded summaries. Keep sample sizes and ties\nvisible. Preparing a report does not\nauthorize new measurement.\n\n## Authority and approval\n\n### Agent operations\n\nUse `canonry_key_self` (CLI `canonry key whoami --format json`) to inspect the\ncurrent credential's scopes, project boundary, and host-derived `operator`\nauthority without exposing its token. Missing `operator` means unapproved.\n`canonry_settings_get` and\n`canonry_telemetry_get` describe the connected server, not the agent's local\nmachine. Telemetry reports configured preference, effective state, and any\nenvironment override; inspecting status never creates an anonymous identifier.\nAfter approval, `canonry_telemetry_update` changes that preference and\n`canonry_provider_settings_update` changes or resets an already-configured\nprovider's model and quota, restores its built-in endpoint, or removes an\noptional Local key. Both require `settings.write`; neither accepts credentials.\nA reset, restoring the endpoint and removing the Local key included, also\nrequires install administrator authority. Pass the `revision` from\n`canonry_provider_settings_get` as `expectedRevision` to reject a stale edit.\nServer telemetry reads and updates additionally require operator authority.\nOrdinary audit-history reads omit internal telemetry events and their state.\n\nOperator authority is deny-by-default and separate from customer admin roles.\nThe deployment owner must approve a dedicated, instance-wide API key's ID in\nthe server environment variable `CANONRY_OPERATOR_KEY_IDS` (comma-separated IDs),\nthen restart the server. Empty/unset approves nobody; wildcards are invalid.\nKeep the bearer private to internal operators; never approve a customer-held or\nshared proxy/bootstrap key. Use `logs.read` for read-only diagnostics, adding\n`settings.write` only when telemetry control is required. Ordinary key creation,\naccount roles, OAuth consent, and caller headers cannot grant operator status.\nRevoking an approved key invalidates it immediately. Host enrollment is a trust\nbootstrap step, intentionally unavailable through customer-facing APIs.\nAPI, CLI, and MCP enforce the same boundary; MCP hides internal tools unless\nthe server confirms operator authority, including in explicit read-only mode.\nProject analytics, research, and normal project permissions are unchanged.\n\n`canonry_logs_list` reads bounded, redacted runtime events from both the\napplication logger and Fastify request/error logging. It requires an\ninstance-wide `logs.read` grant (or wildcard) and a host-approved direct bearer.\nBrowser sessions, OAuth/delegated credentials, customer admins, and project-scoped\nkeys cannot use it, even with a project filter or a matching allowlist ID.\nA `logs.read`-only key is read-only automatically, without a\nsecond `read` marker. Named `*.read` scopes cannot grant mutations; an explicit\nwrite grant is needed and remains subject to its route gates. Returned messages\nare sanitized and bounded; raw request or response bodies, headers, cookies,\nprovider payloads, and stacks are not\npart of the queryable surface. The same secret-redaction policy runs before\nconsole output and storage. Do not deliberately log secrets: redaction is a\ndefense in depth, not permission to put credentials into diagnostic strings.\nOpaque escaped payloads containing secret assignments are omitted when safe\npartial masking cannot be guaranteed; correlate their retained error codes and IDs.\n\nFile-backed hosts retain runtime logs in SQLite across restarts, bounded to\n10,000 events and seven days. In-memory hosts report `retention: \"process\"`.\nFilter by `actor`, `requestId`, `runId`, `projectId`, `module`, `level`, or an\ninclusive `since`/`until` interval. Keep filters unchanged when resuming an\nopaque cursor; retention eviction can invalidate it. Inspect `retentionPolicy`,\n`captureErrors`, `dropped`, `truncated`, and `retention` before drawing\nconclusions. Missing logs are not proof that an action did not happen. Use\n`canonry_project_history` or `canonry_history_global` for persistent audit events;\noffset pages have deterministic ordering but are not snapshots of concurrent writes.\n\nAudit `actor` comes from authenticated identity (`user:<id>` or `api-key:<id>`),\nnot a caller-supplied header. A delegated MCP credential records its originating\nuser as actor and the actual credential in `credentialId`. `requestId` correlates\nHTTP events; `userAgent` and `actorSession` are bounded, untrusted client hints,\nnever identity or permission grants. Older audit rows are not backfilled with\nidentities the server cannot prove.\n\nBoth shipped HTTP hosts issue restart-safe UUID request IDs and return them in\n`x-request-id`. Use that value to correlate a CLI/API failure with log entries;\nHTTP diagnostics retain the method and route template, not raw URL parameters.\nRequest-bound loggers retain completion attribution, while generic background\ncontinuations stop inheriting caller identity after the response completes.\nCapture covers the owning server process after initialization, not arbitrary\nconsole output, other worker processes, or host/container logs. Run one server\ninstance per process and database, as required by the single-tenant deployment\nmodel; this is not a cross-tenant or distributed log collector.\n\nFor CLI use, settings reads are remote. Google setup and telemetry retain their\nlocal defaults: pass `--target server` explicitly to configure the connected\nserver. `schedule list <project>` lists all schedule kinds, and\n`notify events --target server` discovers the server's event catalog.\n\nMCP returns legacy text JSON plus structured results. Objects keep their shape;\narrays use `{items: [...]}` in `structuredContent`, and scalars use `{value: ...}`.\nErrors preserve the existing envelope and CLI exit codes: HTTP 4xx (including\n429 policy limits) use exit 1; HTTP 5xx use exit 2. Server-provided `Retry-After`\nand request IDs are exposed as `retryAfterMs` and `requestId` when available;\nclients do not infer retryability from HTTP 429 or retry automatically.\nFor a write with an ambiguous outcome, inspect saved state or its receipt before\nretrying; a retry hint is not proof that repeating a write is safe.\n\n### Action boundaries\n\nStart with stored evidence. Before a live provider read, sweep, probe, research\nrun, sync, write, or externally visible action, obtain approval covering its\nexact target, action, and bounded work. Approval already given for that exact\noperation need not be asked for again, but does not extend to more projects,\nlarger batches, retries with new identities, or recurring work.\n\nHTTP GET and MCP `readOnlyHint` describe aspects of an operation, not its cost\nor permission. Provider discovery, account reads, and live diagnostics may\nconsume quota even when labeled read-only. If the tool's effect is unclear,\ninspect its description and request direction before calling it.\n\nAuthentication, role/scope checks, project restrictions, quotas, and guarded\napproval receipts are enforced by the server. Help, skills, resources, and tool\nvisibility cannot grant authority. Never change credentials, endpoints, or\nproject identifiers to work around a missing tool or a `403` response.\n\nFor guarded ads writes, inspect unresolved operation receipts before retrying.\nUse the receipt's supported recovery action; do not replay a mutation under a\nnew identity. An executor cannot create or widen its own human approval grant.\nOn ambiguous results, exhausted bounds, or refusal, stop and report what is\nknown and what permission or operator action is needed.\n\n## Version and source\n\nThis public, versioned document is the source for initialization guidance,\nintent routes, the optional resource, and generated Canonry `SKILL.md` files.\nGuide v1 may receive compatible clarifications; incompatible routing contracts\nrequire a new guide version. The running server's help describes its actual\ncatalog and remains usable without fetching this document.\n"
404
410
  };
405
411
 
406
412
  // src/mcp/operations-guide.ts
@@ -1,6 +1,8 @@
1
1
  import {
2
- hostMatchesDomain
3
- } from "./chunk-ZBTUISPG.js";
2
+ CitationStates,
3
+ hostMatchesDomain,
4
+ isCitationMeasurable
5
+ } from "./chunk-O7S7OHLE.js";
4
6
 
5
7
  // src/citation-utils.ts
6
8
  function pickProjectCitedDomain(citedDomains, projectDomains) {
@@ -10,16 +12,17 @@ function pickProjectCitedDomain(citedDomains, projectDomains) {
10
12
  return void 0;
11
13
  }
12
14
  function determineCitationState(normalized, domains) {
15
+ if (!isCitationMeasurable(normalized)) return CitationStates["not-measured"];
13
16
  for (const canonicalDomain of domains) {
14
17
  if (normalized.citedDomains.some((d) => hostMatchesDomain(d, canonicalDomain))) {
15
- return "cited";
18
+ return CitationStates.cited;
16
19
  }
17
20
  for (const source of normalized.groundingSources) {
18
- if (hostMatchesDomain(source.uri, canonicalDomain)) return "cited";
19
- if (source.title && hostMatchesDomain(source.title, canonicalDomain)) return "cited";
21
+ if (hostMatchesDomain(source.uri, canonicalDomain)) return CitationStates.cited;
22
+ if (source.title && hostMatchesDomain(source.title, canonicalDomain)) return CitationStates.cited;
20
23
  }
21
24
  }
22
- return "not-cited";
25
+ return CitationStates["not-cited"];
23
26
  }
24
27
  function computeCitedCompetitorDomains(citedDomains, competitorDomains) {
25
28
  const citedCompetitors = /* @__PURE__ */ new Set();
@@ -28,7 +28,7 @@ import {
28
28
  unknownUsageObservation,
29
29
  usdToMicros,
30
30
  withRetry
31
- } from "./chunk-ZBTUISPG.js";
31
+ } from "./chunk-O7S7OHLE.js";
32
32
 
33
33
  // ../provider-answer-services/src/core/http.ts
34
34
  var MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
@@ -189,9 +189,10 @@ function retryOptions(options, isRetryable) {
189
189
  return {
190
190
  maxRetries: 2,
191
191
  baseDelayMs: 2e3,
192
- isRetryable: (error) => !options.signal?.aborted && isRetryable(error),
192
+ isRetryable,
193
193
  computeDelayMs: (_attempt, error, delay) => retryAfterDelayMs(error) ?? delay,
194
- ...options.sleep ? { sleep: options.sleep } : {}
194
+ ...options.sleep ? { sleep: options.sleep } : {},
195
+ ...options.signal ? { signal: options.signal } : {}
195
196
  };
196
197
  }
197
198
  function requestJson(descriptor, request, options, errorMessage) {
@@ -430,19 +431,20 @@ function createAnswerServiceAdapter(definition, options = {}) {
430
431
  citationMeasurable: raw.citationMeasurable ?? raw.evidence?.citationMeasurable ?? false
431
432
  };
432
433
  }
433
- async function healthcheck(config) {
434
+ async function healthcheck(config, signal) {
434
435
  const problem = configProblem(descriptor, config);
435
436
  if (problem) return { ok: false, provider: config.provider, message: problem };
436
437
  const keyCheck = descriptor.settings.keyCheck;
437
438
  if (!keyCheck) {
438
- return { ok: true, provider: config.provider, message: `${descriptor.displayName} key saved, not verified: the service documents no free key check.` };
439
+ return { ok: true, verified: false, provider: config.provider, message: `${descriptor.displayName} key saved, not verified: the service documents no free key check.` };
439
440
  }
440
441
  try {
441
442
  await requestKeyCheck(descriptor, { method: keyCheck.method, path: keyCheck.path }, {
442
443
  apiKey: config.apiKey ?? "",
443
444
  timeoutMs: 15e3,
444
445
  ...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {},
445
- ...options.sleep ? { sleep: options.sleep } : {}
446
+ ...options.sleep ? { sleep: options.sleep } : {},
447
+ ...signal ? { signal } : {}
446
448
  });
447
449
  return { ok: true, provider: config.provider, message: `${descriptor.displayName} connected` };
448
450
  } catch (error) {
@@ -0,0 +1,31 @@
1
+ import {
2
+ CitationStates
3
+ } from "./chunk-O7S7OHLE.js";
4
+
5
+ // src/snapshot-cell.ts
6
+ var SNAPSHOT_CELL_LEGEND = "(cell = [citation][mention]; C=cited c=not \xB7=not measured, M=mentioned m=not, \u2013=no data)";
7
+ function citationGlyph(state) {
8
+ switch (state) {
9
+ case CitationStates.cited:
10
+ return "C";
11
+ case CitationStates["not-cited"]:
12
+ return "c";
13
+ case CitationStates["not-measured"]:
14
+ return "\xB7";
15
+ }
16
+ }
17
+ function citedBase(total, notMeasured) {
18
+ return total - (notMeasured ?? 0);
19
+ }
20
+ function mentionGlyph(mentioned) {
21
+ if (mentioned === true) return "M";
22
+ if (mentioned === false) return "m";
23
+ return "\u2013";
24
+ }
25
+
26
+ export {
27
+ SNAPSHOT_CELL_LEGEND,
28
+ citationGlyph,
29
+ citedBase,
30
+ mentionGlyph
31
+ };
@@ -5,11 +5,11 @@ import {
5
5
  isStoredPageReadTool,
6
6
  isStoredReadTool,
7
7
  truncateToolResult
8
- } from "./chunk-Q5SNOMRB.js";
8
+ } from "./chunk-56VVRWJM.js";
9
9
  import {
10
10
  CANONRY_MCP_TOOLKITS,
11
11
  resolveAeroSkillDir
12
- } from "./chunk-MTW37GXF.js";
12
+ } from "./chunk-2FWYWGIR.js";
13
13
  import {
14
14
  AGENT_REASONING_LEVELS,
15
15
  aeroStreamFn,
@@ -23,18 +23,18 @@ import {
23
23
  resolveApiKeyFor,
24
24
  resolveModelForProvider,
25
25
  validateAgentProviderRegistry
26
- } from "./chunk-BLW327AU.js";
26
+ } from "./chunk-EE3QTCLS.js";
27
27
  import {
28
28
  CanonryMcpToolNames,
29
29
  canonryMcpTools
30
- } from "./chunk-3GMBHGQ5.js";
30
+ } from "./chunk-F46TBLXK.js";
31
31
  import {
32
32
  createLogger
33
- } from "./chunk-UXXZXXNG.js";
33
+ } from "./chunk-7SNMIXEQ.js";
34
34
  import {
35
35
  agentToolEvents,
36
36
  llmUsageEvents
37
- } from "./chunk-KK3Z34AN.js";
37
+ } from "./chunk-2DV7F6XA.js";
38
38
  import {
39
39
  MeasurementPortfolioLists,
40
40
  SeoCapabilities,
@@ -43,7 +43,7 @@ import {
43
43
  describeError,
44
44
  validationError,
45
45
  visibilityReportRequestSchema
46
- } from "./chunk-ZBTUISPG.js";
46
+ } from "./chunk-O7S7OHLE.js";
47
47
 
48
48
  // src/agent/session.ts
49
49
  import fs2 from "fs";
@@ -503,12 +503,16 @@ function nearToolNames(name, known) {
503
503
  function isReadTool(name) {
504
504
  return name === DISCOVER || name === LOAD || metadata.get(name)?.access === "read";
505
505
  }
506
+ function canonicalToolName(name) {
507
+ return metadata.get(name)?.deprecatedAliasOf ?? name;
508
+ }
506
509
  function correctToolNames(runtime, message, visible) {
507
510
  let known;
508
511
  for (const block of message.content) {
509
512
  if (block.type !== "toolCall" || visible.has(block.name)) continue;
510
513
  known ??= /* @__PURE__ */ new Set([...metadata.keys(), ...runtime.allowed.map((tool) => tool.name), ...visible]);
511
- const matches = nearToolNames(block.name, known);
514
+ const renamed = canonicalToolName(block.name);
515
+ const matches = renamed === block.name ? nearToolNames(block.name, known) : [renamed];
512
516
  if (matches.length !== 1 || !visible.has(matches[0]) || !isReadTool(matches[0])) continue;
513
517
  runtime.corrected.set(block.id, block.name);
514
518
  block.name = matches[0];
@@ -540,6 +544,12 @@ function withCorrectedToolNames(runtime, response, visible, wrapUp, signal) {
540
544
  }
541
545
  function suggestToolName(runtime, name) {
542
546
  const visible = visibleTools(runtime).map((tool) => tool.name);
547
+ const renamed = canonicalToolName(name);
548
+ if (renamed !== name) {
549
+ if (!visible.includes(renamed) && !runtime.allowed.some((tool) => tool.name === renamed)) return void 0;
550
+ const toolkit2 = metadata.get(renamed)?.tier;
551
+ return visible.includes(renamed) || !toolkit2 ? `${name} is now ${renamed}. Call ${renamed} instead.` : `${name} is now ${renamed}. Call ${LOAD} with toolkit "${toolkit2}", then call ${renamed}.`;
552
+ }
543
553
  const matches = nearToolNames(name, /* @__PURE__ */ new Set([...visible, ...runtime.allowed.map((tool) => tool.name)]));
544
554
  if (matches.length === 0 || matches.length > MAX_NAME_SUGGESTIONS || matches.includes(name)) return void 0;
545
555
  if (matches.length > 1) return `${name} is not a tool. Did you mean one of: ${matches.join(", ")}? Call the one you meant by its exact name.`;
@@ -771,11 +781,11 @@ async function readAeroViewEvidence(options) {
771
781
  if (!context.page?.nodeKey) {
772
782
  const overview = await client.getTechnicalAeoCrawl(projectName, context.page);
773
783
  const search2 = new URLSearchParams(overview.runId ? { siteHealthRunId: overview.runId } : {});
774
- return { context, retrievedAt: (/* @__PURE__ */ new Date()).toISOString(), source: { label: "Open Site Health", href: `${base}/projects/${encodeURIComponent(projectName)}/technical-aeo?${search2}` }, overview, instruction: "No page is selected. Ask which page if the prompt is ambiguous. Retrieval time is not the crawl date." };
784
+ return { context, retrievedAt: (/* @__PURE__ */ new Date()).toISOString(), source: { label: "Open Agent Readiness", href: `${base}/projects/${encodeURIComponent(projectName)}/agent-readiness?${search2}` }, overview, instruction: "No page is selected. Ask which page if the prompt is ambiguous. Retrieval time is not the crawl date." };
775
785
  }
776
786
  const audit = await client.getTechnicalAeoPageAudit(projectName, context.page);
777
787
  const search = new URLSearchParams({ nodeKey: context.page.nodeKey, ...audit.runId || context.page.runId ? { runId: audit.runId ?? context.page.runId } : {} });
778
- return { context, retrievedAt: (/* @__PURE__ */ new Date()).toISOString(), source: { label: "Open stored page audit", href: `${base}/api/v1/projects/${encodeURIComponent(projectName)}/technical-aeo/crawl/pages/audit?${search}` }, audit, interpretation: "A stored technical audit is not proof of a cause for mention or citation changes." };
788
+ return { context, retrievedAt: (/* @__PURE__ */ new Date()).toISOString(), source: { label: "Open stored page audit", href: `${base}/api/v1/projects/${encodeURIComponent(projectName)}/agent-readiness/crawl/pages/audit?${search}` }, audit, interpretation: "A stored Agent Readiness audit scan is not proof of a cause for mention or citation changes." };
779
789
  }
780
790
  const request = visibilityReportRequestSchema.parse(context?.selection ?? { queryClass: "all", limit: 10 });
781
791
  const report = await client.getVisibilityReport(projectName, request);
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  USAGE_TELEMETRY_HEADERS,
3
3
  redactLogString
4
- } from "./chunk-ZBTUISPG.js";
4
+ } from "./chunk-O7S7OHLE.js";
5
5
 
6
6
  // ../api-routes/src/request-context.ts
7
7
  import { AsyncLocalStorage } from "async_hooks";
@@ -15,7 +15,7 @@ import {
15
15
  hostMatchesDomain,
16
16
  hostOf,
17
17
  isLocationRedirectStatus
18
- } from "./chunk-ZBTUISPG.js";
18
+ } from "./chunk-O7S7OHLE.js";
19
19
 
20
20
  // ../integration-traffic/src/agent-trust.ts
21
21
  var AGENT_TRUST_RULES_VERSION = 2;
@@ -1,6 +1,11 @@
1
+ import {
2
+ SNAPSHOT_CELL_LEGEND,
3
+ citationGlyph,
4
+ mentionGlyph
5
+ } from "./chunk-J6536OL6.js";
1
6
  import {
2
7
  createApiClient
3
- } from "./chunk-OL2L5ANJ.js";
8
+ } from "./chunk-UGO2FO6V.js";
4
9
  import {
5
10
  emitJsonl
6
11
  } from "./chunk-Z7IUAJOF.js";
@@ -10,7 +15,6 @@ import {
10
15
  isMachineFormat
11
16
  } from "./chunk-HGMYEDAQ.js";
12
17
  import {
13
- CitationStates,
14
18
  MentionIdentities,
15
19
  OUTSTANDING_PROVIDER_BATCH_STATUSES,
16
20
  ProviderBatchStatuses,
@@ -19,12 +23,38 @@ import {
19
23
  formatMicros,
20
24
  formatRunErrorOneLine,
21
25
  resolveProviderInput
22
- } from "./chunk-ZBTUISPG.js";
26
+ } from "./chunk-O7S7OHLE.js";
23
27
 
24
28
  // src/commands/run.ts
25
29
  function getClient() {
26
30
  return createApiClient();
27
31
  }
32
+ async function previewRun(project, format) {
33
+ const preview = await getClient().previewRun(project);
34
+ if (format === "jsonl") {
35
+ emitJsonl([preview]);
36
+ return;
37
+ }
38
+ if (format === "json") {
39
+ console.log(JSON.stringify(preview, null, 2));
40
+ return;
41
+ }
42
+ const reasons = {
43
+ "unpublished-plan": "Publish the Advanced Measurement draft first.",
44
+ "no-queries": "Add queries first.",
45
+ "no-provider": "Connect an answer provider first.",
46
+ "providers-failing": "Fix the held provider accounts first.",
47
+ "run-in-progress": `A sweep is already active (${preview.activeRunId}).`
48
+ };
49
+ console.log(`Sweep preview: ${project}${preview.revision === null ? "" : ` \xB7 published revision ${preview.revision}`}`);
50
+ console.log(` ${preview.queryCount} queries${preview.assignmentCount === null ? "" : ` \xB7 ${preview.assignmentCount} assignments`}`);
51
+ for (const population of preview.queryClasses) console.log(` ${population.queryCount} ${population.queryClass} queries${population.assignmentCount === null ? "" : ` \xB7 ${population.assignmentCount} assignments`}`);
52
+ console.log(` ${preview.requestCount} runnable requests \xB7 ${preview.expectedRequestCount} planned answers`);
53
+ for (const engine of preview.engines) console.log(` ${engine.provider}${engine.models.length ? ` (${engine.models.join(", ")})` : ""}: ${engine.requestCount} requests${engine.available ? "" : " \xB7 unavailable"}`);
54
+ for (const location of preview.locations) console.log(` Location: ${location.location ?? "No location"} \xB7 ${location.requestCount} requests`);
55
+ for (const [provider, held] of Object.entries(preview.admission.providers)) console.log(` Held: ${providerAccountStreakLine(provider, held)}`);
56
+ console.log(preview.blockedReason === null ? " Ready to start." : ` ${reasons[preview.blockedReason]}`);
57
+ }
28
58
  var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "partial", "failed", "cancelled"]);
29
59
  function shellArg(value) {
30
60
  return /^[\w.-]+$/.test(value) ? value : `'${value.replaceAll("'", `'\\''`)}'`;
@@ -444,6 +474,9 @@ function printRunDetail(run) {
444
474
  if (run.createdAt) console.log(` Created: ${run.createdAt}`);
445
475
  const skipped = Object.entries(run.skippedProviders ?? {});
446
476
  for (const [provider, streak] of skipped) console.log(` Skipped: ${providerAccountStreakLine(provider, streak)}`);
477
+ for (const [provider, failure] of Object.entries(run.providerFailures ?? {})) {
478
+ console.log(` Failing: ${provider}: ${failure.failedQueries} ${failure.failedQueries === 1 ? "query" : "queries"} failed${failure.stopped ? ", no longer called this run" : ""}. ${failure.message}`);
479
+ }
447
480
  if (run.error) {
448
481
  if (run.error.message) console.log(` Error: ${run.error.message}`);
449
482
  if (run.error.providers) {
@@ -462,15 +495,13 @@ function printRunDetail(run) {
462
495
  if (run.usage && run.usage.length > 0) printUsageTable(run.usage);
463
496
  if (run.snapshots && run.snapshots.length > 0) {
464
497
  console.log(`
465
- Snapshots: ${run.snapshots.length} (cell = [citation][mention]; C=cited c=not, M=mentioned m=not, \u2013=no data)`);
498
+ Snapshots: ${run.snapshots.length} ${SNAPSHOT_CELL_LEGEND}`);
466
499
  let differentSubject = 0;
467
500
  for (const s of run.snapshots) {
468
- const citationGlyph = s.citationState === CitationStates.cited ? "C" : "c";
469
- const mentionGlyph = typeof s.answerMentioned === "boolean" ? s.answerMentioned ? "M" : "m" : "\u2013";
470
501
  const modelLabel = s.model ? ` (${s.model})` : "";
471
502
  const ruledOut = s.mentionIdentity === MentionIdentities["wrong-subject"];
472
503
  if (ruledOut) differentSubject++;
473
- console.log(` [${citationGlyph}${mentionGlyph}] ${s.provider}${modelLabel} ${s.query}${ruledOut ? " \xB7 different subject" : ""}`);
504
+ console.log(` [${citationGlyph(s.citationState)}${mentionGlyph(s.answerMentioned)}] ${s.provider}${modelLabel} ${s.query}${ruledOut ? " \xB7 different subject" : ""}`);
474
505
  }
475
506
  if (differentSubject > 0) {
476
507
  console.log(" \xB7 different subject: the answer uses your name, but sentiment found it is about something else, so it is not counted as mentioned.");
@@ -604,6 +635,7 @@ async function fillRun(runId, opts = {}) {
604
635
  }
605
636
 
606
637
  export {
638
+ previewRun,
607
639
  providerAccountStreakLine,
608
640
  runAdmissionLines,
609
641
  triggerRun,