@canonry/canonry 7.32.0 → 7.43.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/assets/agent-workspace/skills/canonry/references/aeo-analysis.md +24 -0
  2. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +204 -4
  3. package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +14 -2
  4. package/assets/assets/{AuditHistoryPanel-cQyVA85P.js → AuditHistoryPanel-C5oZsvsR.js} +1 -1
  5. package/assets/assets/{BacklinksPage-UGZ_9N7F.js → BacklinksPage-CF5sGSTQ.js} +1 -1
  6. package/assets/assets/{HistoryPage-UJVFZ5LK.js → HistoryPage-DumL6cl5.js} +1 -1
  7. package/assets/assets/{MeasurementPropertyPage-ChpOxQfx.js → MeasurementPropertyPage-DCegOp_w.js} +3 -3
  8. package/assets/assets/ProjectPage-CWDk48Y6.js +8 -0
  9. package/assets/assets/RunsPage-Bb1VMWYu.js +1 -0
  10. package/assets/assets/{SettingsPage-DeKxjuTr.js → SettingsPage-CHGh169l.js} +1 -1
  11. package/assets/assets/{SiteHealthSection-COeVsgop.js → SiteHealthSection-bGzH9m9-.js} +3 -3
  12. package/assets/assets/{TrafficPage-DmMmeJKK.js → TrafficPage-cfnEZ2Mu.js} +1 -1
  13. package/assets/assets/{TrafficSourceDetailPage-CUehseZe.js → TrafficSourceDetailPage-BQS0MAXE.js} +1 -1
  14. package/assets/assets/{extract-error-message-BsxdS7Po.js → extract-error-message-IWhACkO_.js} +1 -1
  15. package/assets/assets/index-BDY3-V1S.js +86 -0
  16. package/assets/assets/{react-sigma_core.esm.min--MbbBWzJ.js → react-sigma_core.esm.min-BQt189AI.js} +1 -1
  17. package/assets/assets/{v2-overview-adapter-D-IScpdp.js → v2-overview-adapter-ZC3VuoTT.js} +1 -1
  18. package/assets/index.html +1 -1
  19. package/dist/{chunk-WRHA54BN.js → chunk-65BHDVYO.js} +5604 -3984
  20. package/dist/{chunk-FW4GX2LO.js → chunk-BPFRGC7K.js} +3 -3
  21. package/dist/{chunk-2C6NWFN4.js → chunk-BWR5MNL5.js} +2 -2
  22. package/dist/chunk-C2I7M3B2.js +22948 -0
  23. package/dist/{chunk-42XUY243.js → chunk-E43JM3QV.js} +11430 -10405
  24. package/dist/chunk-HGMYEDAQ.js +79 -0
  25. package/dist/{chunk-QFXD7GR5.js → chunk-KOIOZ7QW.js} +193 -19
  26. package/dist/{chunk-ZIETXJYU.js → chunk-LYKDNJXR.js} +158 -104
  27. package/dist/chunk-WRUKKEGR.js +562 -0
  28. package/dist/chunk-WU2BRWXY.js +546 -0
  29. package/dist/{chunk-K5EGSSXC.js → chunk-WXX3NL4C.js} +108 -41
  30. package/dist/cli-catalog-J3O6W726.js +15 -0
  31. package/dist/cli-commands-QST2CPDG.js +15 -0
  32. package/dist/cli.js +136 -22525
  33. package/dist/{demo-server-G5OK7SWE.js → demo-server-KA7DWTY6.js} +5 -4
  34. package/dist/index.js +7 -6
  35. package/dist/{intelligence-service-3ZUVTIO7.js → intelligence-service-LHZZTJPH.js} +2 -2
  36. package/dist/mcp.js +30 -15
  37. package/package.json +10 -10
  38. package/assets/assets/ProjectPage-CvxuPSiC.js +0 -8
  39. package/assets/assets/RunsPage-C6U_YcDY.js +0 -1
  40. package/assets/assets/index-ahEr6Oc-.js +0 -86
@@ -0,0 +1,79 @@
1
+ // src/support-links.ts
2
+ var CANONRY_DISCORD_URL = "https://discord.gg/jnJ2H2z5Xh";
3
+ var CANONRY_ISSUES_URL = "https://github.com/Canonry/canonry/issues";
4
+
5
+ // src/cli-error.ts
6
+ function isMachineFormat(format) {
7
+ return format === "json" || format === "jsonl";
8
+ }
9
+ var EXIT_USER_ERROR = 1;
10
+ var EXIT_SYSTEM_ERROR = 2;
11
+ var CliError = class extends Error {
12
+ code;
13
+ displayMessage;
14
+ details;
15
+ exitCode;
16
+ constructor(options) {
17
+ super(options.message);
18
+ this.name = "CliError";
19
+ this.code = options.code;
20
+ this.displayMessage = options.displayMessage;
21
+ this.details = options.details;
22
+ this.exitCode = options.exitCode ?? EXIT_USER_ERROR;
23
+ }
24
+ };
25
+ function usageError(displayMessage, options) {
26
+ const firstLine = displayMessage.split("\n", 1)[0] ?? "Error: invalid command usage";
27
+ return new CliError({
28
+ code: "CLI_USAGE_ERROR",
29
+ message: options?.message ?? firstLine.replace(/^Error:\s*/, ""),
30
+ displayMessage,
31
+ details: options?.details
32
+ });
33
+ }
34
+ function isEndpointMissing(err) {
35
+ if (!(err instanceof CliError)) return false;
36
+ const status = err.details?.httpStatus;
37
+ return status === 404 || status === 405;
38
+ }
39
+ function systemError(message, options) {
40
+ return new CliError({
41
+ code: "CLI_SYSTEM_ERROR",
42
+ message,
43
+ displayMessage: options?.displayMessage,
44
+ details: options?.details,
45
+ exitCode: EXIT_SYSTEM_ERROR
46
+ });
47
+ }
48
+ function printCliError(err, format) {
49
+ if (isMachineFormat(format)) {
50
+ const envelope = err instanceof CliError ? { error: { code: err.code, message: err.message, ...err.details ? { details: err.details } : {} } } : { error: { code: "CLI_ERROR", message: err instanceof Error ? err.message : "An unexpected error occurred" } };
51
+ console.error(JSON.stringify(envelope, null, format === "jsonl" ? 0 : 2));
52
+ return;
53
+ }
54
+ if (err instanceof CliError && err.displayMessage) {
55
+ console.error(err.displayMessage);
56
+ } else if (err instanceof Error) {
57
+ console.error(`Error: ${err.message}`);
58
+ } else {
59
+ console.error("An unexpected error occurred");
60
+ }
61
+ const unexpected = !(err instanceof CliError) || err.exitCode === EXIT_SYSTEM_ERROR;
62
+ if (unexpected && process.stderr.isTTY) {
63
+ console.error(`
64
+ Need help? Run 'canonry support' or ask in the Canonry Discord: ${CANONRY_DISCORD_URL}`);
65
+ }
66
+ }
67
+
68
+ export {
69
+ CANONRY_DISCORD_URL,
70
+ CANONRY_ISSUES_URL,
71
+ isMachineFormat,
72
+ EXIT_USER_ERROR,
73
+ EXIT_SYSTEM_ERROR,
74
+ CliError,
75
+ usageError,
76
+ isEndpointMissing,
77
+ systemError,
78
+ printCliError
79
+ };
@@ -1,11 +1,14 @@
1
+ import {
2
+ PACKAGE_VERSION,
3
+ canonryMcpTools,
4
+ toolOperationAccess
5
+ } from "./chunk-LYKDNJXR.js";
1
6
  import {
2
7
  CliError,
3
8
  EXIT_SYSTEM_ERROR,
4
9
  EXIT_USER_ERROR,
5
- PACKAGE_VERSION,
6
- canonryMcpTools,
7
10
  isMachineFormat
8
- } from "./chunk-ZIETXJYU.js";
11
+ } from "./chunk-HGMYEDAQ.js";
9
12
  import {
10
13
  AGENT_PROVIDER_IDS,
11
14
  CANONRY_NPM_PACKAGE_URL,
@@ -13,8 +16,11 @@ import {
13
16
  DEFAULT_VIEWER_RESEARCH_DAILY_RUN_LIMIT,
14
17
  OPERATIONAL_LOG_FIELDS_HEADER,
15
18
  OPERATIONAL_LOG_OPT_IN_CONTEXT_FIELDS,
19
+ OPERATION_ACCESS_HEADER,
20
+ OperationAccesses,
16
21
  RESEARCH_RUN_SCOPE,
17
22
  SKILL_MANIFEST_FILENAME,
23
+ STORED_ONLY_HEADER,
18
24
  SkillsClients,
19
25
  USAGE_TELEMETRY_HEADERS,
20
26
  agentPluginClientLabel,
@@ -31,8 +37,10 @@ import {
31
37
  isRetiredAgentProviderId,
32
38
  isSensitiveDiagnosticQueryKey,
33
39
  isStrictSemver,
40
+ matchOperationTemplate,
34
41
  normalizeAgentSlug,
35
42
  normalizeTelemetryStatus,
43
+ operationAccess,
36
44
  operationalLogListReadSchema,
37
45
  providerBatchConfigSchema,
38
46
  providerPricingSchema,
@@ -49,7 +57,7 @@ import {
49
57
  skillsClientSchema,
50
58
  upgradeCaveatFor,
51
59
  upgradeCommandFor
52
- } from "./chunk-42XUY243.js";
60
+ } from "./chunk-E43JM3QV.js";
53
61
 
54
62
  // src/config.ts
55
63
  import fs from "fs";
@@ -1505,6 +1513,22 @@ var getApiV1ProjectsByName = (options) => {
1505
1513
  ...options
1506
1514
  });
1507
1515
  };
1516
+ var patchApiV1ProjectsByName = (options) => {
1517
+ return (options.client ?? client).patch({
1518
+ security: [
1519
+ {
1520
+ scheme: "bearer",
1521
+ type: "http"
1522
+ }
1523
+ ],
1524
+ url: "/api/v1/projects/{name}",
1525
+ ...options,
1526
+ headers: {
1527
+ "Content-Type": "application/json",
1528
+ ...options.headers
1529
+ }
1530
+ });
1531
+ };
1508
1532
  var putApiV1ProjectsByName = (options) => {
1509
1533
  return (options.client ?? client).put({
1510
1534
  security: [
@@ -2949,6 +2973,34 @@ var getApiV1ProjectsByNameAnalyticsCompetitors = (options) => {
2949
2973
  ...options
2950
2974
  });
2951
2975
  };
2976
+ var getApiV1ProjectsByNameCompetitorSuggestions = (options) => {
2977
+ return (options.client ?? client).get({
2978
+ security: [
2979
+ {
2980
+ scheme: "bearer",
2981
+ type: "http"
2982
+ }
2983
+ ],
2984
+ url: "/api/v1/projects/{name}/competitor-suggestions",
2985
+ ...options
2986
+ });
2987
+ };
2988
+ var postApiV1ProjectsByNameCompetitorChangesPreview = (options) => {
2989
+ return (options.client ?? client).post({
2990
+ security: [
2991
+ {
2992
+ scheme: "bearer",
2993
+ type: "http"
2994
+ }
2995
+ ],
2996
+ url: "/api/v1/projects/{name}/competitor-changes/preview",
2997
+ ...options,
2998
+ headers: {
2999
+ "Content-Type": "application/json",
3000
+ ...options.headers
3001
+ }
3002
+ });
3003
+ };
2952
3004
  var getApiV1ProjectsByNameRuns = (options) => {
2953
3005
  return (options.client ?? client).get({
2954
3006
  security: [
@@ -3057,6 +3109,22 @@ var postApiV1Apply = (options) => {
3057
3109
  }
3058
3110
  });
3059
3111
  };
3112
+ var postApiV1ApplyPreview = (options) => {
3113
+ return (options.client ?? client).post({
3114
+ security: [
3115
+ {
3116
+ scheme: "bearer",
3117
+ type: "http"
3118
+ }
3119
+ ],
3120
+ url: "/api/v1/apply/preview",
3121
+ ...options,
3122
+ headers: {
3123
+ "Content-Type": "application/json",
3124
+ ...options.headers
3125
+ }
3126
+ });
3127
+ };
3060
3128
  var getApiV1ProjectsByNameHistory = (options) => {
3061
3129
  return (options.client ?? client).get({
3062
3130
  security: [
@@ -5245,6 +5313,18 @@ var getApiV1ProjectsByNameGaProperties = (options) => {
5245
5313
  ...options
5246
5314
  });
5247
5315
  };
5316
+ var getApiV1ProjectsByNameGaAcquisition = (options) => {
5317
+ return (options.client ?? client).get({
5318
+ security: [
5319
+ {
5320
+ scheme: "bearer",
5321
+ type: "http"
5322
+ }
5323
+ ],
5324
+ url: "/api/v1/projects/{name}/ga/acquisition",
5325
+ ...options
5326
+ });
5327
+ };
5248
5328
  var getApiV1ProjectsByNameGaMeasurementAnalysis = (options) => {
5249
5329
  return (options.client ?? client).get({
5250
5330
  security: [
@@ -5629,6 +5709,18 @@ var getApiV1Doctor = (options) => {
5629
5709
  ...options
5630
5710
  });
5631
5711
  };
5712
+ var getApiV1Status = (options) => {
5713
+ return (options?.client ?? client).get({
5714
+ security: [
5715
+ {
5716
+ scheme: "bearer",
5717
+ type: "http"
5718
+ }
5719
+ ],
5720
+ url: "/api/v1/status",
5721
+ ...options
5722
+ });
5723
+ };
5632
5724
  var getApiV1ProjectsByNameDoctor = (options) => {
5633
5725
  return (options.client ?? client).get({
5634
5726
  security: [
@@ -5977,6 +6069,18 @@ var getApiV1ProjectsByNameTrafficReferralAssessment = (options) => {
5977
6069
  ...options
5978
6070
  });
5979
6071
  };
6072
+ var getApiV1ProjectsByNameTrafficSamples = (options) => {
6073
+ return (options.client ?? client).get({
6074
+ security: [
6075
+ {
6076
+ scheme: "bearer",
6077
+ type: "http"
6078
+ }
6079
+ ],
6080
+ url: "/api/v1/projects/{name}/traffic/samples",
6081
+ ...options
6082
+ });
6083
+ };
5980
6084
  var getApiV1ProjectsByNameTrafficEvents = (options) => {
5981
6085
  return (options.client ?? client).get({
5982
6086
  security: [
@@ -6477,6 +6581,30 @@ function runWithUsageTags(client2, tags, fn) {
6477
6581
  if (!(client2 instanceof ApiClient)) return fn();
6478
6582
  return usageTagStore.run(tags, fn);
6479
6583
  }
6584
+ function classifyStoredOnlyRequest(method, url) {
6585
+ const operation = matchOperationTemplate(method, url);
6586
+ const separator = operation.indexOf(" ");
6587
+ return {
6588
+ operation,
6589
+ access: operationAccess(operation.slice(0, separator), operation.slice(separator + 1), OperationAccesses.live)
6590
+ };
6591
+ }
6592
+ function storedOnlyRequestHeaders(method, url) {
6593
+ if (!["1", "true"].includes((process.env.CANONRY_STORED_ONLY ?? "").toLowerCase())) return {};
6594
+ const { operation, access } = classifyStoredOnlyRequest(method, url);
6595
+ if (access !== OperationAccesses.stored) {
6596
+ throw new CliError({
6597
+ code: "STORED_ONLY_REFUSED",
6598
+ message: `Stored-only mode refuses ${access} operation ${operation}`,
6599
+ details: { operation, access },
6600
+ exitCode: EXIT_USER_ERROR
6601
+ });
6602
+ }
6603
+ return { [STORED_ONLY_HEADER]: "1" };
6604
+ }
6605
+ function storedOnlyProbeHeaders() {
6606
+ return ["1", "true"].includes((process.env.CANONRY_STORED_ONLY ?? "").toLowerCase()) ? { [STORED_ONLY_HEADER]: "1" } : {};
6607
+ }
6480
6608
  var SAFE_CLIENT_NAME = /^\w[\w./-]{0,127}$/;
6481
6609
  var SAFE_ACTOR_SESSION = /^\w[\w.:-]{0,127}$/;
6482
6610
  function safeClientName(value) {
@@ -6548,6 +6676,7 @@ var ApiClient = class {
6548
6676
  };
6549
6677
  this.heyClient = createClient2({ baseUrl: this.originUrl, apiKey: this.apiKey, headers: this.requestHeaders });
6550
6678
  this.heyClient.interceptors.request.use((request) => {
6679
+ for (const [key, value] of Object.entries(storedOnlyRequestHeaders(request.method, request.url.toString()))) request.headers.set(key, value);
6551
6680
  const tags = usageTagStore.getStore();
6552
6681
  if (tags?.mcpTool) request.headers.set(USAGE_TELEMETRY_HEADERS.mcpTool, tags.mcpTool);
6553
6682
  if (tags?.mcpCall) request.headers.set(USAGE_TELEMETRY_HEADERS.mcpCall, tags.mcpCall);
@@ -6569,6 +6698,7 @@ var ApiClient = class {
6569
6698
  try {
6570
6699
  const origin = new URL(this.originUrl).origin;
6571
6700
  const res = await fetch(`${origin}/health`, {
6701
+ headers: storedOnlyProbeHeaders(),
6572
6702
  signal: AbortSignal.timeout(2e3)
6573
6703
  });
6574
6704
  if (res.ok) {
@@ -6626,7 +6756,7 @@ var ApiClient = class {
6626
6756
  if (traceEnabled) {
6627
6757
  const durMs = Date.now() - traceStart;
6628
6758
  process.stderr.write(
6629
- `[trace] ${result.request.method} ${redactRequestTarget(result.request.url)} \u2192 ${result.response.status} (${durMs}ms)
6759
+ `[trace] ${result.request.method} ${redactRequestTarget(result.request.url)} \u2192 ${result.response.status} (${durMs}ms) access=${result.response.headers.get(OPERATION_ACCESS_HEADER) ?? "unknown"}
6630
6760
  `
6631
6761
  );
6632
6762
  }
@@ -6729,10 +6859,13 @@ var ApiClient = class {
6729
6859
  * and releases the response when done.
6730
6860
  */
6731
6861
  async streamPost(path3, body, signal) {
6862
+ const candidateUrl = `${this.originUrl}/api/v1${path3}`;
6863
+ const storedOnlyHeaders = storedOnlyRequestHeaders("POST", candidateUrl);
6732
6864
  await this.probeBasePath();
6733
6865
  const url = `${this.originUrl}/api/v1${path3}`;
6734
6866
  const headers = {
6735
6867
  ...this.requestHeaders,
6868
+ ...storedOnlyHeaders,
6736
6869
  "Content-Type": "application/json",
6737
6870
  Accept: "text/event-stream"
6738
6871
  };
@@ -6772,6 +6905,11 @@ var ApiClient = class {
6772
6905
  () => putApiV1ProjectsByName({ client: this.heyClient, path: { name }, body })
6773
6906
  );
6774
6907
  }
6908
+ async patchProject(name, body) {
6909
+ return this.invoke(
6910
+ () => patchApiV1ProjectsByName({ client: this.heyClient, path: { name }, body })
6911
+ );
6912
+ }
6775
6913
  async listProjects() {
6776
6914
  return this.invoke(() => getApiV1Projects({ client: this.heyClient }));
6777
6915
  }
@@ -6786,19 +6924,26 @@ var ApiClient = class {
6786
6924
  () => getApiV1ProjectsByNameDeletePreview({ client: this.heyClient, path: { name } })
6787
6925
  );
6788
6926
  }
6789
- async getStatus(project) {
6927
+ async getStatus(opts) {
6790
6928
  return this.invoke(
6791
- () => getApiV1ProjectsByName({ client: this.heyClient, path: { name: project } })
6929
+ () => getApiV1Status({ client: this.heyClient, query: { project: opts?.project, problems: opts?.problems } })
6792
6930
  );
6793
6931
  }
6794
- async getExport(project) {
6932
+ async getExport(project, opts) {
6795
6933
  return this.invoke(
6796
- () => getApiV1ProjectsByNameExport({ client: this.heyClient, path: { name: project } })
6934
+ () => getApiV1ProjectsByNameExport({
6935
+ client: this.heyClient,
6936
+ path: { name: project },
6937
+ query: { includeSecrets: opts?.includeSecrets }
6938
+ })
6797
6939
  );
6798
6940
  }
6799
6941
  async apply(config) {
6800
6942
  return this.invoke(() => postApiV1Apply({ client: this.heyClient, body: config }));
6801
6943
  }
6944
+ async previewApply(config) {
6945
+ return this.invoke(() => postApiV1ApplyPreview({ client: this.heyClient, body: config }));
6946
+ }
6802
6947
  async getProjectOverview(project, opts) {
6803
6948
  return this.invoke(
6804
6949
  () => getApiV1ProjectsByNameOverview({
@@ -6834,10 +6979,11 @@ var ApiClient = class {
6834
6979
  if (opts.until) params.set("until", opts.until);
6835
6980
  if (opts.includeProbes) params.set("includeProbes", "true");
6836
6981
  const url = `${this.originUrl}/api/v1/projects/${encodeURIComponent(project)}/results/export?${params.toString()}`;
6982
+ const storedOnlyHeaders = storedOnlyRequestHeaders("GET", url);
6837
6983
  let res;
6838
6984
  try {
6839
6985
  res = await fetch(url, {
6840
- headers: { ...this.requestHeaders }
6986
+ headers: { ...this.requestHeaders, ...storedOnlyHeaders }
6841
6987
  });
6842
6988
  } catch (err) {
6843
6989
  const message = describeError(err);
@@ -6941,13 +7087,27 @@ var ApiClient = class {
6941
7087
  () => getApiV1ProjectsByNameCompetitors({ client: this.heyClient, path: { name: project } })
6942
7088
  );
6943
7089
  }
7090
+ async suggestCompetitors(project, query = {}) {
7091
+ return this.invoke(
7092
+ () => getApiV1ProjectsByNameCompetitorSuggestions({
7093
+ client: this.heyClient,
7094
+ path: { name: project },
7095
+ query
7096
+ })
7097
+ );
7098
+ }
7099
+ async previewCompetitorChange(project, request) {
7100
+ return this.invoke(
7101
+ () => postApiV1ProjectsByNameCompetitorChangesPreview({ client: this.heyClient, path: { name: project }, body: request })
7102
+ );
7103
+ }
6944
7104
  /** Each entry is a domain or `{ domain, aliases }`; stated aliases are added to that competitor's list. */
6945
- async appendCompetitors(project, competitors) {
7105
+ async appendCompetitors(project, competitors, options) {
6946
7106
  return this.invoke(
6947
7107
  () => postApiV1ProjectsByNameCompetitors({
6948
7108
  client: this.heyClient,
6949
7109
  path: { name: project },
6950
- body: { competitors }
7110
+ body: { competitors, ...options?.acknowledgeBulkAdd !== void 0 ? { acknowledgeBulkAdd: options.acknowledgeBulkAdd } : {} }
6951
7111
  })
6952
7112
  );
6953
7113
  }
@@ -7519,13 +7679,13 @@ var ApiClient = class {
7519
7679
  })
7520
7680
  );
7521
7681
  }
7522
- async listRuns(project, limit, kind, status, excludeKind, excludeKinds) {
7682
+ async listRuns(project, limit, kind, status, excludeKind, excludeKinds, since) {
7523
7683
  return this.invoke(
7524
7684
  () => getApiV1ProjectsByNameRuns({
7525
7685
  client: this.heyClient,
7526
7686
  path: { name: project },
7527
7687
  // Filters arrive as free CLI strings; the server validates them against the enums.
7528
- query: { limit, kind, status, excludeKind, excludeKinds }
7688
+ query: { limit, kind, status, excludeKind, excludeKinds, since }
7529
7689
  })
7530
7690
  );
7531
7691
  }
@@ -7641,6 +7801,7 @@ var ApiClient = class {
7641
7801
  * never run the CLI about a newer release.
7642
7802
  */
7643
7803
  async getServerUpdateAvailable() {
7804
+ if (storedOnlyProbeHeaders()[STORED_ONLY_HEADER]) return null;
7644
7805
  try {
7645
7806
  await this.probeBasePath();
7646
7807
  const res = await fetch(`${this.originUrl}/health`, { signal: AbortSignal.timeout(2e3) });
@@ -8653,6 +8814,9 @@ var ApiClient = class {
8653
8814
  () => getApiV1ProjectsByNameGaProperties({ client: this.heyClient, path: { name: project } })
8654
8815
  );
8655
8816
  }
8817
+ async gaAcquisition(project, query) {
8818
+ return this.invoke(() => getApiV1ProjectsByNameGaAcquisition({ client: this.heyClient, path: { name: project }, query }));
8819
+ }
8656
8820
  async gaMeasurementAnalysis(project, params) {
8657
8821
  return this.invoke(
8658
8822
  () => getApiV1ProjectsByNameGaMeasurementAnalysis({
@@ -8852,6 +9016,9 @@ var ApiClient = class {
8852
9016
  query
8853
9017
  }));
8854
9018
  }
9019
+ async trafficListSamples(project, query) {
9020
+ return this.invoke(() => getApiV1ProjectsByNameTrafficSamples({ client: this.heyClient, path: { name: project }, query }));
9021
+ }
8855
9022
  async trafficListEvents(project, params) {
8856
9023
  return this.invoke(
8857
9024
  () => getApiV1ProjectsByNameTrafficEvents({
@@ -9884,6 +10051,7 @@ var DynamicToolCatalog = class {
9884
10051
  eager: this.eager,
9885
10052
  loadedToolkits: [...this.loaded].sort(),
9886
10053
  coreTools: this.entries.filter((entry) => entry.tool.tier === "core").map((entry) => entry.tool.name),
10054
+ liveTools: this.entries.filter((entry) => entry.tool.tier === "core" && toolOperationAccess(entry.tool) === OperationAccesses.live).map((entry) => entry.tool.name),
9887
10055
  toolkits: CANONRY_MCP_TOOLKITS.map((toolkit) => this.toolkitEntry(toolkit)).filter((entry) => entry.toolCount > 0),
9888
10056
  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."
9889
10057
  };
@@ -9897,6 +10065,7 @@ var DynamicToolCatalog = class {
9897
10065
  whenToLoad: toolkit.whenToLoad,
9898
10066
  toolCount: tools.length,
9899
10067
  tools,
10068
+ liveTools: this.entries.filter((entry) => entry.tool.tier === toolkit.name && toolOperationAccess(entry.tool) === OperationAccesses.live).map((entry) => entry.tool.name),
9900
10069
  loaded: this.loaded.has(toolkit.name)
9901
10070
  };
9902
10071
  }
@@ -9929,7 +10098,7 @@ var OPERATIONS_GUIDE = {
9929
10098
  "guideVersion": "v1",
9930
10099
  "operationsGuideUrl": "https://github.com/Canonry/canonry/blob/main/docs/agent-operations/v1.md",
9931
10100
  "resourceUri": "canonry://agent-operations/v1",
9932
- "initialize": 'Canonry tracks how AI answer engines mention brands and cite domains.\nStart with canonry_help({intent:"status"}), or name your intended workflow. It returns a small route of currently available tools and approval boundaries. Follow its stored-evidence reads before proposing action. No skill, resource reader, plugin installation, or local CLI is required for connected MCP clients. A host-native Canonry skill, if installed, is 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 explicit approval for live provider reads, sweeps, probes, research, syncs, and writes, bounded to the target and requested work. A readOnlyHint or GET is not proof of a free operation. Live ads reads include canonry_ads_account, canonry_ads_geo_search, canonry_ads_live_delivery, canonry_ads_conversion_pixels, canonry_ads_conversion_event_settings. Google live reads/syncs include canonry_google_ads_customers, canonry_gtm_accounts, canonry_gtm_containers, canonry_gtm_workspaces, canonry_google_ads_sync, canonry_gtm_sync.\nHelp and skills grant no permissions. The server enforces authority. On missing tools or 403, report the boundary; do not switch credentials or endpoints to bypass it. Hosted catalogs are fixed; only use canonry_load_toolkit when help explicitly offers it.\nIf you uncover a Canonry struggle (failure, confusing output, workaround) or a concrete improvement, tell the user and send it once with canonry_feedback. No secrets or customer data.\n',
10101
+ "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',
9933
10102
  "approvalBoundary": [
9934
10103
  "provider reads",
9935
10104
  "sweeps",
@@ -9962,10 +10131,11 @@ var OPERATIONS_GUIDE = {
9962
10131
  "health"
9963
10132
  ],
9964
10133
  "next": [
10134
+ "canonry_status",
9965
10135
  "canonry_projects_list",
9966
10136
  "canonry_project_overview"
9967
10137
  ],
9968
- "guidance": "Select an accessible project, then read its stored overview. Report freshness and missing evidence; do not start a sweep to fill a gap."
10138
+ "guidance": "Read canonry_status first for stored integration health, freshness, and repeated failures across accessible projects, then inspect a project overview for detail. Report missing evidence; do not start a sweep or sync to fill a gap."
9969
10139
  },
9970
10140
  "diagnose": {
9971
10141
  "keywords": [
@@ -10030,12 +10200,13 @@ var OPERATIONS_GUIDE = {
10030
10200
  "gtm"
10031
10201
  ],
10032
10202
  "next": [
10203
+ "canonry_status",
10033
10204
  "canonry_projects_list",
10034
10205
  "canonry_project_get",
10035
10206
  "canonry_ga_status",
10036
10207
  "canonry_google_ads_status"
10037
10208
  ],
10038
- "guidance": "Inspect stored connection status and snapshot freshness. Use the currently listed specialist tools for stored evidence. Connecting, selecting resources, syncing, and live provider reads require scoped approval; never request credentials in chat."
10209
+ "guidance": "Read canonry_status first for the stored instance or project health view, then use specialist tools for detail. Connecting, selecting resources, syncing, and live provider reads require scoped approval; never request credentials in chat."
10039
10210
  },
10040
10211
  "reports": {
10041
10212
  "keywords": [
@@ -10071,6 +10242,8 @@ function operationsHelp(catalog, mode, intent = "", includeCatalog = false) {
10071
10242
  }
10072
10243
  const wanted = [...reads, ...requestedActions];
10073
10244
  const loadToolkits = mode === "stdio-progressive" ? catalog.toolkits.filter((t) => !t.loaded && t.tools.some((name) => wanted.includes(name))).map((t) => t.name) : [];
10245
+ const liveTools = [...catalog.liveTools];
10246
+ for (const toolkit of catalog.toolkits) liveTools.push(...toolkit.liveTools);
10074
10247
  return {
10075
10248
  guideVersion: OPERATIONS_GUIDE.guideVersion,
10076
10249
  mode,
@@ -10085,7 +10258,8 @@ function operationsHelp(catalog, mode, intent = "", includeCatalog = false) {
10085
10258
  authority: OPERATIONS_GUIDE.authority,
10086
10259
  operationsGuideUrl: OPERATIONS_GUIDE.operationsGuideUrl,
10087
10260
  ...loadToolkits.length ? { loadToolkits } : {},
10088
- ...includeCatalog ? catalog : {}
10261
+ ...includeCatalog ? catalog : {},
10262
+ liveTools: [...new Set(liveTools)].sort()
10089
10263
  };
10090
10264
  }
10091
10265
 
@@ -10209,7 +10383,7 @@ function getCanonryMcpTools(scope = "all", tiers, credentialScopes, operator = f
10209
10383
  const readOnly = scope === "read-only" || credentialScopes !== void 0 && isReadOnlyKey(credentialScopes);
10210
10384
  const restricted = credentialScopes && restrictedWriteScopes(credentialScopes);
10211
10385
  const authorized = canonryMcpTools.filter((tool) => !tool.requiresOperator || operator);
10212
- const byScope = readOnly ? authorized.filter((tool) => tool.access === "read") : authorized.filter((tool) => {
10386
+ const byScope = scope === "stored-only" ? authorized.filter((tool) => toolOperationAccess(tool) === OperationAccesses.stored) : readOnly ? authorized.filter((tool) => tool.access === "read") : authorized.filter((tool) => {
10213
10387
  if (!restricted || tool.access === "read") return true;
10214
10388
  if (tool.requiredScope && restricted.includes(tool.requiredScope)) return true;
10215
10389
  return restricted.some((grant) => grant !== RESEARCH_RUN_SCOPE) && tool.tier === "ads";