@canonry/canonry 7.78.3 → 7.79.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.
- package/assets/agent-workspace/skills/aero/references/agent-operations.md +6 -0
- package/assets/agent-workspace/skills/canonry/SKILL.md +6 -0
- package/dist/{chunk-LOMRNRML.js → chunk-4UXTNBXS.js} +1 -1
- package/dist/{chunk-OES6PSQK.js → chunk-GL5HD74G.js} +21 -39
- package/dist/{chunk-WVCDUKJ2.js → chunk-JI7FDO7I.js} +19 -0
- package/dist/{chunk-QALET4XH.js → chunk-QYBHWA76.js} +4 -4
- package/dist/{chunk-KHIZSTDP.js → chunk-R7HQNCG5.js} +4 -4
- package/dist/{chunk-BKOG3GEI.js → chunk-RPTOVEZT.js} +79 -9
- package/dist/{cli-commands-6M5QDGBZ.js → cli-commands-DEY6C7JD.js} +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.js +4 -4
- package/dist/{init-FLWVOJHU.js → init-7VNM45TF.js} +1 -1
- package/dist/mcp.js +2 -2
- package/dist/{serve-7HN3VT6J.js → serve-XAI5YFWR.js} +4 -4
- package/dist/{session-FBHHWILQ.js → session-5ZH53SET.js} +4 -3
- package/package.json +1 -1
|
@@ -36,6 +36,12 @@ no provider calls, reads no project data, and changes no permissions.
|
|
|
36
36
|
requires approval; listing an action does not authorize or execute it.
|
|
37
37
|
`includeCatalog: true` additionally returns toolkit details when needed.
|
|
38
38
|
|
|
39
|
+
Analysis routes also return `playbooks`: Canonry's analyst playbooks for that
|
|
40
|
+
work, the same files its built-in agent reads. Read each one before
|
|
41
|
+
interpreting results. `canonry_help({ playbook: "<slug>" })` returns its full
|
|
42
|
+
text, a note on using it over MCP, and every available slug. Playbooks are
|
|
43
|
+
guidance, not permission.
|
|
44
|
+
|
|
39
45
|
Hosted connections use a fixed catalog. Help only suggests tools offered by that
|
|
40
46
|
connection; it never tells a hosted agent to dynamically load a toolkit. A
|
|
41
47
|
progressive local stdio connection may return `loadToolkits`: call
|
|
@@ -27,6 +27,12 @@ no provider calls, reads no project data, and changes no permissions.
|
|
|
27
27
|
requires approval; listing an action does not authorize or execute it.
|
|
28
28
|
`includeCatalog: true` additionally returns toolkit details when needed.
|
|
29
29
|
|
|
30
|
+
Analysis routes also return `playbooks`: Canonry's analyst playbooks for that
|
|
31
|
+
work, the same files its built-in agent reads. Read each one before
|
|
32
|
+
interpreting results. `canonry_help({ playbook: "<slug>" })` returns its full
|
|
33
|
+
text, a note on using it over MCP, and every available slug. Playbooks are
|
|
34
|
+
guidance, not permission.
|
|
35
|
+
|
|
30
36
|
Hosted connections use a fixed catalog. Help only suggests tools offered by that
|
|
31
37
|
connection; it never tells a hosted agent to dynamically load a toolkit. A
|
|
32
38
|
progressive local stdio connection may return `loadToolkits`: call
|
|
@@ -47,7 +47,7 @@ function queueServeHandoff() {
|
|
|
47
47
|
async function runPendingServeHandoff(format) {
|
|
48
48
|
if (!pendingServeHandoff) return;
|
|
49
49
|
pendingServeHandoff = false;
|
|
50
|
-
const { serveCommand } = await import("./serve-
|
|
50
|
+
const { serveCommand } = await import("./serve-XAI5YFWR.js");
|
|
51
51
|
await serveCommand(format);
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -7,8 +7,9 @@ import {
|
|
|
7
7
|
truncateToolResult
|
|
8
8
|
} from "./chunk-IYMTQHPD.js";
|
|
9
9
|
import {
|
|
10
|
-
CANONRY_MCP_TOOLKITS
|
|
11
|
-
|
|
10
|
+
CANONRY_MCP_TOOLKITS,
|
|
11
|
+
resolveAeroSkillDir
|
|
12
|
+
} from "./chunk-JI7FDO7I.js";
|
|
12
13
|
import {
|
|
13
14
|
AGENT_REASONING_LEVELS,
|
|
14
15
|
aeroStreamFn,
|
|
@@ -45,8 +46,8 @@ import {
|
|
|
45
46
|
} from "./chunk-PM2DB3HE.js";
|
|
46
47
|
|
|
47
48
|
// src/agent/session.ts
|
|
48
|
-
import
|
|
49
|
-
import
|
|
49
|
+
import fs2 from "fs";
|
|
50
|
+
import path2 from "path";
|
|
50
51
|
import { Agent } from "@earendil-works/pi-agent-core";
|
|
51
52
|
|
|
52
53
|
// src/agent-config.ts
|
|
@@ -75,27 +76,9 @@ function resolveAgentReasoning(env, config, provider, modelId) {
|
|
|
75
76
|
return config.agent?.reasoning ?? defaultAgentReasoning(provider, modelId);
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
// src/agent/skill-
|
|
79
|
+
// src/agent/skill-tools.ts
|
|
79
80
|
import fs from "fs";
|
|
80
81
|
import path from "path";
|
|
81
|
-
import { fileURLToPath } from "url";
|
|
82
|
-
function resolveAeroSkillDir(pkgDir) {
|
|
83
|
-
const here = pkgDir ?? path.dirname(fileURLToPath(import.meta.url));
|
|
84
|
-
const candidates = [
|
|
85
|
-
path.join(here, "../assets/agent-workspace/skills/aero"),
|
|
86
|
-
path.join(here, "../../assets/agent-workspace/skills/aero"),
|
|
87
|
-
path.join(here, "../../../../skills/aero")
|
|
88
|
-
];
|
|
89
|
-
for (const candidate of candidates) {
|
|
90
|
-
if (fs.existsSync(path.join(candidate, "SKILL.md"))) return candidate;
|
|
91
|
-
}
|
|
92
|
-
throw new Error(`Aero skill not found. Searched:
|
|
93
|
-
${candidates.join("\n ")}`);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// src/agent/skill-tools.ts
|
|
97
|
-
import fs2 from "fs";
|
|
98
|
-
import path2 from "path";
|
|
99
82
|
import { Type } from "@earendil-works/pi-ai";
|
|
100
83
|
var MAX_DOC_CHARS = 2e4;
|
|
101
84
|
function textResult(details) {
|
|
@@ -116,13 +99,13 @@ function parseDescription(body) {
|
|
|
116
99
|
return "(no description)";
|
|
117
100
|
}
|
|
118
101
|
function scanSkillDocs(skillDir) {
|
|
119
|
-
const refsDir =
|
|
120
|
-
if (!
|
|
102
|
+
const refsDir = path.join(skillDir ?? resolveAeroSkillDir(), "references");
|
|
103
|
+
if (!fs.existsSync(refsDir)) return [];
|
|
121
104
|
const entries = [];
|
|
122
|
-
for (const file of
|
|
105
|
+
for (const file of fs.readdirSync(refsDir)) {
|
|
123
106
|
if (!file.endsWith(".md")) continue;
|
|
124
|
-
const filePath =
|
|
125
|
-
const body =
|
|
107
|
+
const filePath = path.join(refsDir, file);
|
|
108
|
+
const body = fs.readFileSync(filePath, "utf-8");
|
|
126
109
|
entries.push({
|
|
127
110
|
slug: file.replace(/\.md$/, ""),
|
|
128
111
|
description: parseDescription(body),
|
|
@@ -165,8 +148,8 @@ function buildReadSkillDocTool() {
|
|
|
165
148
|
availableSlugs: docs.map((d) => d.slug)
|
|
166
149
|
});
|
|
167
150
|
}
|
|
168
|
-
const filePath =
|
|
169
|
-
const content =
|
|
151
|
+
const filePath = path.join(skillDir, "references", `${match.slug}.md`);
|
|
152
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
170
153
|
if (content.length > MAX_DOC_CHARS) {
|
|
171
154
|
return textResult({
|
|
172
155
|
slug: match.slug,
|
|
@@ -360,9 +343,9 @@ function resolvePageReferences(runtime, tool, params) {
|
|
|
360
343
|
}
|
|
361
344
|
return resolved;
|
|
362
345
|
}
|
|
363
|
-
function pageOwner(value,
|
|
346
|
+
function pageOwner(value, path3) {
|
|
364
347
|
let owner = value;
|
|
365
|
-
for (const key of
|
|
348
|
+
for (const key of path3) {
|
|
366
349
|
if (owner === null || typeof owner !== "object" || Array.isArray(owner)) return void 0;
|
|
367
350
|
owner = owner[key];
|
|
368
351
|
}
|
|
@@ -397,8 +380,8 @@ function referencePages(runtime, tool, params, value) {
|
|
|
397
380
|
});
|
|
398
381
|
owner[cursor.key] = reference;
|
|
399
382
|
const pagination = pageOwner(root.__pagination, []);
|
|
400
|
-
for (const [
|
|
401
|
-
if (typeof note === "string") pagination[
|
|
383
|
+
for (const [path3, note] of Object.entries(pagination ?? {})) {
|
|
384
|
+
if (typeof note === "string") pagination[path3] = note.split(token).join(reference);
|
|
402
385
|
}
|
|
403
386
|
changed = true;
|
|
404
387
|
}
|
|
@@ -927,9 +910,9 @@ function ensureBuiltinsRegistered() {
|
|
|
927
910
|
}
|
|
928
911
|
function loadAeroSystemPrompt(pkgDir, opts = {}) {
|
|
929
912
|
const skillDir = resolveAeroSkillDir(pkgDir);
|
|
930
|
-
const skillBody =
|
|
931
|
-
const soulPath =
|
|
932
|
-
const base =
|
|
913
|
+
const skillBody = fs2.readFileSync(path2.join(skillDir, "SKILL.md"), "utf-8");
|
|
914
|
+
const soulPath = path2.join(skillDir, "soul.md");
|
|
915
|
+
const base = fs2.existsSync(soulPath) ? `${fs2.readFileSync(soulPath, "utf-8").trimEnd()}
|
|
933
916
|
|
|
934
917
|
---
|
|
935
918
|
|
|
@@ -942,7 +925,7 @@ function appendSystemPromptExtras(base, env = process.env) {
|
|
|
942
925
|
const filePath = env.AERO_SYSTEM_PROMPT_FILE?.trim();
|
|
943
926
|
if (filePath) {
|
|
944
927
|
try {
|
|
945
|
-
fileBody =
|
|
928
|
+
fileBody = fs2.readFileSync(filePath, "utf-8").trim();
|
|
946
929
|
} catch {
|
|
947
930
|
fileBody = "";
|
|
948
931
|
}
|
|
@@ -1070,7 +1053,6 @@ export {
|
|
|
1070
1053
|
resolveAgentAllowViewers,
|
|
1071
1054
|
resolveAgentReasoning,
|
|
1072
1055
|
AeroLlmUsageFeatures,
|
|
1073
|
-
resolveAeroSkillDir,
|
|
1074
1056
|
buildSkillDocTools,
|
|
1075
1057
|
configureAeroRuntime,
|
|
1076
1058
|
isSystemMessage,
|
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
// src/agent/skill-paths.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
function resolveAeroSkillDir(pkgDir) {
|
|
6
|
+
const here = pkgDir ?? path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const candidates = [
|
|
8
|
+
path.join(here, "../assets/agent-workspace/skills/aero"),
|
|
9
|
+
path.join(here, "../../assets/agent-workspace/skills/aero"),
|
|
10
|
+
path.join(here, "../../../../skills/aero")
|
|
11
|
+
];
|
|
12
|
+
for (const candidate of candidates) {
|
|
13
|
+
if (fs.existsSync(path.join(candidate, "SKILL.md"))) return candidate;
|
|
14
|
+
}
|
|
15
|
+
throw new Error(`Aero skill not found. Searched:
|
|
16
|
+
${candidates.join("\n ")}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
1
19
|
// src/mcp/toolkits.ts
|
|
2
20
|
var CANONRY_MCP_TIERS = [
|
|
3
21
|
"seo",
|
|
@@ -125,6 +143,7 @@ function isCanonryMcpToolkitName(value) {
|
|
|
125
143
|
}
|
|
126
144
|
|
|
127
145
|
export {
|
|
146
|
+
resolveAeroSkillDir,
|
|
128
147
|
CANONRY_MCP_TIERS,
|
|
129
148
|
CANONRY_MCP_TOOLKIT_NAMES,
|
|
130
149
|
CANONRY_MCP_TOOLKITS,
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
resolveConfiguredAgentProvider,
|
|
28
28
|
resolveSessionProviderAndModel,
|
|
29
29
|
setAeroSystemPrompt
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-GL5HD74G.js";
|
|
31
31
|
import {
|
|
32
32
|
AeroToolProfiles,
|
|
33
33
|
AeroToolScopes,
|
|
@@ -99,11 +99,11 @@ import {
|
|
|
99
99
|
} from "./chunk-4GEEYY3N.js";
|
|
100
100
|
import {
|
|
101
101
|
createCanonryMcpServer
|
|
102
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-RPTOVEZT.js";
|
|
103
103
|
import {
|
|
104
104
|
CANONRY_MCP_TIERS,
|
|
105
105
|
CANONRY_MCP_TOOLKIT_NAMES
|
|
106
|
-
} from "./chunk-
|
|
106
|
+
} from "./chunk-JI7FDO7I.js";
|
|
107
107
|
import {
|
|
108
108
|
ApiClient
|
|
109
109
|
} from "./chunk-7OAXEMOF.js";
|
|
@@ -5937,7 +5937,7 @@ function nonBlank(value) {
|
|
|
5937
5937
|
return trimmed ? trimmed : void 0;
|
|
5938
5938
|
}
|
|
5939
5939
|
function resolveBuildCommit(env = process.env) {
|
|
5940
|
-
const embedded = true ? "
|
|
5940
|
+
const embedded = true ? "4ef4905de19fbd19f9571174a291d4ef01463cbb" : void 0;
|
|
5941
5941
|
return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
|
|
5942
5942
|
}
|
|
5943
5943
|
function resolveInstanceIdentity(env = process.env) {
|
|
@@ -6868,7 +6868,7 @@ var SYSTEM_CLI_COMMANDS = [
|
|
|
6868
6868
|
options: { access: stringOption() },
|
|
6869
6869
|
run: async (input) => {
|
|
6870
6870
|
if (input.positionals.length > 1) throw usageError("Expected at most one command group");
|
|
6871
|
-
const [{ printCliCatalog }, { REGISTERED_CLI_COMMANDS: REGISTERED_CLI_COMMANDS2 }] = await Promise.all([import("./cli-catalog-KC6JYHSW.js"), import("./cli-commands-
|
|
6871
|
+
const [{ printCliCatalog }, { REGISTERED_CLI_COMMANDS: REGISTERED_CLI_COMMANDS2 }] = await Promise.all([import("./cli-catalog-KC6JYHSW.js"), import("./cli-commands-DEY6C7JD.js")]);
|
|
6872
6872
|
printCliCatalog(REGISTERED_CLI_COMMANDS2, input.format, input.positionals[0], getString(input.values, "access"));
|
|
6873
6873
|
}
|
|
6874
6874
|
},
|
|
@@ -6917,7 +6917,7 @@ var SYSTEM_CLI_COMMANDS = [
|
|
|
6917
6917
|
},
|
|
6918
6918
|
allowPositionals: false,
|
|
6919
6919
|
run: async (input) => {
|
|
6920
|
-
const { initCommand } = await import("./init-
|
|
6920
|
+
const { initCommand } = await import("./init-7VNM45TF.js");
|
|
6921
6921
|
await initCommand({
|
|
6922
6922
|
force: getBoolean(input.values, "force"),
|
|
6923
6923
|
ref: getString(input.values, "ref"),
|
|
@@ -6972,7 +6972,7 @@ var SYSTEM_CLI_COMMANDS = [
|
|
|
6972
6972
|
allowPositionals: false,
|
|
6973
6973
|
run: async (input) => {
|
|
6974
6974
|
applyServerEnv(input.values);
|
|
6975
|
-
const { serveCommand } = await import("./serve-
|
|
6975
|
+
const { serveCommand } = await import("./serve-XAI5YFWR.js");
|
|
6976
6976
|
await serveCommand(input.format);
|
|
6977
6977
|
}
|
|
6978
6978
|
},
|
|
@@ -8290,7 +8290,7 @@ var AGENT_CLI_COMMANDS = [
|
|
|
8290
8290
|
"timeout-ms": stringOption()
|
|
8291
8291
|
},
|
|
8292
8292
|
run: async (input) => {
|
|
8293
|
-
const { coerceAgentProvider } = await import("./session-
|
|
8293
|
+
const { coerceAgentProvider } = await import("./session-5ZH53SET.js");
|
|
8294
8294
|
const { AERO_TOOL_PROFILES, isAeroToolProfile } = await import("./tools-3ACR2D2V.js");
|
|
8295
8295
|
const { agentAsk } = await import("./agent-ask-32RTOL2M.js");
|
|
8296
8296
|
const usage = `canonry agent ask <project> "<prompt>" [--provider ${AGENT_PROVIDER_IDS.join("|")}] [--model <id>] [--scope all|read-only] [--profile default|ads-operator] [--context <json>] [--conversation <id>] [--max-tool-calls <n>] [--timeout-ms <ms>] [--format json]`;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CANONRY_MCP_TOOLKITS,
|
|
3
3
|
CANONRY_MCP_TOOLKIT_NAMES,
|
|
4
|
-
isCanonryMcpToolkitName
|
|
5
|
-
|
|
4
|
+
isCanonryMcpToolkitName,
|
|
5
|
+
resolveAeroSkillDir
|
|
6
|
+
} from "./chunk-JI7FDO7I.js";
|
|
6
7
|
import {
|
|
7
8
|
createApiClient,
|
|
8
9
|
runWithUsageTags
|
|
@@ -125,8 +126,8 @@ function formatZodIssue(issue) {
|
|
|
125
126
|
function zodErrorMessage(error) {
|
|
126
127
|
const first = error.issues[0];
|
|
127
128
|
if (!first) return "Input validation failed";
|
|
128
|
-
const
|
|
129
|
-
return
|
|
129
|
+
const path2 = first.path.map(String).join(".");
|
|
130
|
+
return path2 ? `${path2}: ${first.message}` : first.message;
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
// src/mcp/dynamic-catalog.ts
|
|
@@ -239,6 +240,8 @@ var OPERATIONS_GUIDE = {
|
|
|
239
240
|
],
|
|
240
241
|
"approvalRule": "Get explicit approval for the exact target, action, and bounded provider work before crossing a boundary. Existing approval covers only its stated scope; stop on refusal or ambiguous results.",
|
|
241
242
|
"authority": "Guidance and tool visibility are not permission grants. Server-side roles, scopes, project boundaries, quotas, and approval receipts remain authoritative. Never bypass a refusal with different credentials or endpoints.",
|
|
243
|
+
"playbookRule": 'Read each listed playbook with canonry_help({playbook:"<slug>"}) before interpreting results.',
|
|
244
|
+
"playbookNote": `Shared with Canonry's built-in agent and CLI hosts. Where it names aero_* tools, read_skill_doc or shell cnry commands, use the matching canonry_* tool on this connection. Read a reference it names (references/<slug>.md) with canonry_help({playbook:"<slug>"}). Playbooks are guidance, not permission.`,
|
|
242
245
|
"workflows": {
|
|
243
246
|
"seo": {
|
|
244
247
|
"keywords": [
|
|
@@ -258,6 +261,9 @@ var OPERATIONS_GUIDE = {
|
|
|
258
261
|
"canonry_seo_sync",
|
|
259
262
|
"canonry_seo_run_cancel"
|
|
260
263
|
],
|
|
264
|
+
"playbooks": [
|
|
265
|
+
"seo-analysis"
|
|
266
|
+
],
|
|
261
267
|
"guidance": "Read saved SEO evidence first. Preserve profile, exact domain or host, market, language, plan revision, dataset and collection date. Market rankings (canonry_seo_market_rankings) are the site's Google position for its tracked queries by market, the same queries AI visibility measures; start with canonry_seo_market_summary for counts by outcome, and treat failed or unsearched queries as missing data. SEO traffic and keyword metrics are third-party estimates, separate from Search Console observations and AI citation and mention coverage. Refresh requires explicit approval for the profile and work, a fresh refresh intent and an idempotency key. Hosted financial controls are internal; no credential or billing information belongs in the response."
|
|
262
268
|
},
|
|
263
269
|
"operations": {
|
|
@@ -288,6 +294,9 @@ var OPERATIONS_GUIDE = {
|
|
|
288
294
|
"canonry_projects_list",
|
|
289
295
|
"canonry_project_overview"
|
|
290
296
|
],
|
|
297
|
+
"playbooks": [
|
|
298
|
+
"aero"
|
|
299
|
+
],
|
|
291
300
|
"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."
|
|
292
301
|
},
|
|
293
302
|
"diagnose": {
|
|
@@ -304,6 +313,10 @@ var OPERATIONS_GUIDE = {
|
|
|
304
313
|
"canonry_project_overview",
|
|
305
314
|
"canonry_project_history"
|
|
306
315
|
],
|
|
316
|
+
"playbooks": [
|
|
317
|
+
"aero",
|
|
318
|
+
"regression-playbook"
|
|
319
|
+
],
|
|
307
320
|
"guidance": "Compare stored history in the same project, period, provider/model, location, and query-class scope. Separate observed changes from possible causes; propose any live verification for approval."
|
|
308
321
|
},
|
|
309
322
|
"prospecting": {
|
|
@@ -338,6 +351,10 @@ var OPERATIONS_GUIDE = {
|
|
|
338
351
|
"canonry_measurement_setup",
|
|
339
352
|
"canonry_research_runs_list"
|
|
340
353
|
],
|
|
354
|
+
"playbooks": [
|
|
355
|
+
"portfolio-analysis",
|
|
356
|
+
"aeo-discovery"
|
|
357
|
+
],
|
|
341
358
|
"guidance": "Inspect existing measurement before editing it. Preserve Simple versus Advanced scope, Property/Target identity, market, provider/model, and query class. Direct research submits final queries in one context; reviewed batches submit explicit destinations under one retry key. Expand patterns client-side: scope records a destination and never rewrites queries or fans out a group. Research is separate from tracking; publication does not authorize a sweep."
|
|
342
359
|
},
|
|
343
360
|
"integrations": {
|
|
@@ -359,6 +376,9 @@ var OPERATIONS_GUIDE = {
|
|
|
359
376
|
"canonry_ga_status",
|
|
360
377
|
"canonry_google_ads_status"
|
|
361
378
|
],
|
|
379
|
+
"playbooks": [
|
|
380
|
+
"ai-traffic-tracking"
|
|
381
|
+
],
|
|
362
382
|
"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."
|
|
363
383
|
},
|
|
364
384
|
"reports": {
|
|
@@ -373,14 +393,18 @@ var OPERATIONS_GUIDE = {
|
|
|
373
393
|
"canonry_project_overview",
|
|
374
394
|
"canonry_organic_evidence"
|
|
375
395
|
],
|
|
396
|
+
"playbooks": [
|
|
397
|
+
"aero",
|
|
398
|
+
"reporting"
|
|
399
|
+
],
|
|
376
400
|
"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."
|
|
377
401
|
}
|
|
378
402
|
},
|
|
379
|
-
"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\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"
|
|
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"
|
|
380
404
|
};
|
|
381
405
|
|
|
382
406
|
// src/mcp/operations-guide.ts
|
|
383
|
-
function operationsHelp(catalog, mode, intent = "", includeCatalog = false) {
|
|
407
|
+
function operationsHelp(catalog, mode, intent = "", includeCatalog = false, playbooks = []) {
|
|
384
408
|
const workflows = Object.keys(OPERATIONS_GUIDE.workflows);
|
|
385
409
|
const words = new Set(intent.toLowerCase().split(/[^a-z0-9-]+/));
|
|
386
410
|
const workflow = workflows.find((name) => OPERATIONS_GUIDE.workflows[name].keywords.some((word) => words.has(word))) ?? "status";
|
|
@@ -388,8 +412,10 @@ function operationsHelp(catalog, mode, intent = "", includeCatalog = false) {
|
|
|
388
412
|
const available = /* @__PURE__ */ new Set([...catalog.coreTools, ...catalog.toolkits.filter((t) => t.loaded).flatMap((t) => t.tools)]);
|
|
389
413
|
const reads = route.next;
|
|
390
414
|
const requestedActions = "actions" in route ? route.actions : [];
|
|
415
|
+
const routePlaybooks = "playbooks" in route ? route.playbooks : [];
|
|
391
416
|
const next = reads.filter((name) => available.has(name)).slice(0, 4);
|
|
392
417
|
const actions = requestedActions.filter((name) => available.has(name));
|
|
418
|
+
const listedPlaybooks = routePlaybooks.filter((slug) => playbooks.includes(slug));
|
|
393
419
|
if (reads.includes("canonry_projects_list") && next.length < 2 && available.has("canonry_project_overview") && !next.includes("canonry_project_overview")) {
|
|
394
420
|
next.push("canonry_project_overview");
|
|
395
421
|
}
|
|
@@ -406,6 +432,7 @@ function operationsHelp(catalog, mode, intent = "", includeCatalog = false) {
|
|
|
406
432
|
...actions.length ? { actions } : {},
|
|
407
433
|
workflows,
|
|
408
434
|
guidance: route.guidance,
|
|
435
|
+
...listedPlaybooks.length ? { playbooks: listedPlaybooks, playbookRule: OPERATIONS_GUIDE.playbookRule } : {},
|
|
409
436
|
approvalBoundary: OPERATIONS_GUIDE.approvalBoundary,
|
|
410
437
|
approvalRule: OPERATIONS_GUIDE.approvalRule,
|
|
411
438
|
authority: OPERATIONS_GUIDE.authority,
|
|
@@ -416,6 +443,45 @@ function operationsHelp(catalog, mode, intent = "", includeCatalog = false) {
|
|
|
416
443
|
};
|
|
417
444
|
}
|
|
418
445
|
|
|
446
|
+
// src/mcp/playbooks.ts
|
|
447
|
+
import fs from "fs";
|
|
448
|
+
import path from "path";
|
|
449
|
+
var PLAYBOOK_SLUG = /^[a-z0-9-]{1,64}$/;
|
|
450
|
+
var SKILL_SLUG = "aero";
|
|
451
|
+
var GUIDE_SLUG = "agent-operations";
|
|
452
|
+
function availablePlaybooks() {
|
|
453
|
+
const dir = aeroSkillDir();
|
|
454
|
+
if (!dir) return [];
|
|
455
|
+
const references = path.join(dir, "references");
|
|
456
|
+
const slugs = fs.existsSync(references) ? fs.readdirSync(references).filter((file) => file.endsWith(".md")).map((file) => file.slice(0, -".md".length)).filter((slug) => PLAYBOOK_SLUG.test(slug) && slug !== SKILL_SLUG).sort() : [];
|
|
457
|
+
return [SKILL_SLUG, ...slugs];
|
|
458
|
+
}
|
|
459
|
+
function readPlaybook(slug) {
|
|
460
|
+
const playbooks = availablePlaybooks();
|
|
461
|
+
const dir = aeroSkillDir();
|
|
462
|
+
if (!dir || !playbooks.includes(slug)) {
|
|
463
|
+
throw new CliError({
|
|
464
|
+
code: "NOT_FOUND",
|
|
465
|
+
message: `No playbook "${slug}". Use a slug from availablePlaybooks.`,
|
|
466
|
+
details: { availablePlaybooks: playbooks }
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
const file = slug === SKILL_SLUG ? "SKILL.md" : path.join("references", `${slug}.md`);
|
|
470
|
+
const content = slug === GUIDE_SLUG ? OPERATIONS_GUIDE.markdown : fs.readFileSync(path.join(dir, file), "utf8");
|
|
471
|
+
return `Playbook: ${slug}
|
|
472
|
+
Note: ${OPERATIONS_GUIDE.playbookNote}
|
|
473
|
+
Available: ${playbooks.join(", ")}
|
|
474
|
+
|
|
475
|
+
${content}`;
|
|
476
|
+
}
|
|
477
|
+
function aeroSkillDir() {
|
|
478
|
+
try {
|
|
479
|
+
return resolveAeroSkillDir();
|
|
480
|
+
} catch {
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
419
485
|
// src/mcp/server.ts
|
|
420
486
|
function createCanonryMcpServer(options = {}) {
|
|
421
487
|
return createCanonryMcpServerWithCatalog(options).server;
|
|
@@ -496,23 +562,27 @@ var loadToolkitInputSchema = z.object({
|
|
|
496
562
|
});
|
|
497
563
|
var helpInputSchema = z.object({
|
|
498
564
|
intent: z.string().max(200).optional().describe("Workflow (status, diagnose, prospecting, measurement, integrations, reports) or a short task description."),
|
|
499
|
-
includeCatalog: z.boolean().optional().describe("Include full toolkit details. Omit for a compact, actionable route.")
|
|
565
|
+
includeCatalog: z.boolean().optional().describe("Include full toolkit details. Omit for a compact, actionable route."),
|
|
566
|
+
playbook: z.string().regex(PLAYBOOK_SLUG).optional().describe('Slug from a help result\'s `playbooks`, such as "regression-playbook". Returns that analyst playbook in full instead of a route.')
|
|
500
567
|
});
|
|
501
568
|
function registerMetaTools(server, catalog, opts) {
|
|
502
569
|
server.registerTool(
|
|
503
570
|
"canonry_help",
|
|
504
571
|
{
|
|
505
572
|
title: "Guide a Canonry workflow",
|
|
506
|
-
description: "Start here: route an intent to available stored-evidence tools, separately listed actions requiring approval, and
|
|
573
|
+
description: "Start here: route an intent to available stored-evidence tools, separately listed actions requiring approval, workflow guidance, and the analyst playbooks to read. Pass `playbook` to read one in full. No provider calls or installation required. Optionally include full toolkit details.",
|
|
507
574
|
inputSchema: helpInputSchema,
|
|
508
575
|
annotations: { readOnlyHint: true, idempotentHint: true, destructiveHint: false, openWorldHint: false }
|
|
509
576
|
},
|
|
510
577
|
async (input) => {
|
|
511
578
|
try {
|
|
512
579
|
const parsed = helpInputSchema.parse(input ?? {});
|
|
580
|
+
if (parsed.playbook !== void 0) {
|
|
581
|
+
return { content: [{ type: "text", text: readPlaybook(parsed.playbook) }] };
|
|
582
|
+
}
|
|
513
583
|
const update = readUpdateAvailable(opts.updateAvailable);
|
|
514
584
|
const result = {
|
|
515
|
-
...operationsHelp(catalog.helpResult(), opts.mode, parsed.intent, parsed.includeCatalog),
|
|
585
|
+
...operationsHelp(catalog.helpResult(), opts.mode, parsed.intent, parsed.includeCatalog, availablePlaybooks()),
|
|
516
586
|
...update ? { updateAvailable: updateNoticePayload(update) } : {}
|
|
517
587
|
};
|
|
518
588
|
return { ...jsonToolResult(result), structuredContent: result };
|
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node --import tsx
|
|
2
2
|
import {
|
|
3
3
|
REGISTERED_CLI_COMMANDS
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-R7HQNCG5.js";
|
|
5
5
|
import {
|
|
6
6
|
buildSetupState,
|
|
7
7
|
runPendingServeHandoff
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-4UXTNBXS.js";
|
|
9
9
|
import "./chunk-6GO5VVAC.js";
|
|
10
10
|
import "./chunk-2EWZ3DJ4.js";
|
|
11
11
|
import "./chunk-VJWDBFSL.js";
|
package/dist/index.js
CHANGED
|
@@ -3,9 +3,9 @@ import {
|
|
|
3
3
|
createGoogleMarketingCredentialStore,
|
|
4
4
|
createGoogleMarketingRuntime,
|
|
5
5
|
createServer
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-QYBHWA76.js";
|
|
7
7
|
import "./chunk-WSWHLKKJ.js";
|
|
8
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-GL5HD74G.js";
|
|
9
9
|
import "./chunk-IYMTQHPD.js";
|
|
10
10
|
import "./chunk-ELCLGWUT.js";
|
|
11
11
|
import "./chunk-XEBKF4N5.js";
|
|
@@ -15,8 +15,8 @@ import "./chunk-PLLDJUG2.js";
|
|
|
15
15
|
import "./chunk-TSENU4GG.js";
|
|
16
16
|
import "./chunk-FCYGY4FT.js";
|
|
17
17
|
import "./chunk-4GEEYY3N.js";
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
18
|
+
import "./chunk-RPTOVEZT.js";
|
|
19
|
+
import "./chunk-JI7FDO7I.js";
|
|
20
20
|
import "./chunk-7OAXEMOF.js";
|
|
21
21
|
import {
|
|
22
22
|
loadConfig
|
package/dist/mcp.js
CHANGED
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
} from "./chunk-BLZCNL3B.js";
|
|
4
4
|
import {
|
|
5
5
|
createCanonryMcpServer
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-RPTOVEZT.js";
|
|
7
|
+
import "./chunk-JI7FDO7I.js";
|
|
8
8
|
import {
|
|
9
9
|
createApiClient
|
|
10
10
|
} from "./chunk-7OAXEMOF.js";
|
|
@@ -9,9 +9,9 @@ import {
|
|
|
9
9
|
allowsKeylessFirstRunSetup,
|
|
10
10
|
createServer,
|
|
11
11
|
waitForServerRuntimeStartup
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-QYBHWA76.js";
|
|
13
13
|
import "./chunk-WSWHLKKJ.js";
|
|
14
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-GL5HD74G.js";
|
|
15
15
|
import "./chunk-IYMTQHPD.js";
|
|
16
16
|
import "./chunk-ELCLGWUT.js";
|
|
17
17
|
import "./chunk-XEBKF4N5.js";
|
|
@@ -35,8 +35,8 @@ import {
|
|
|
35
35
|
setTelemetrySource,
|
|
36
36
|
trackEvent
|
|
37
37
|
} from "./chunk-4GEEYY3N.js";
|
|
38
|
-
import "./chunk-
|
|
39
|
-
import "./chunk-
|
|
38
|
+
import "./chunk-RPTOVEZT.js";
|
|
39
|
+
import "./chunk-JI7FDO7I.js";
|
|
40
40
|
import "./chunk-7OAXEMOF.js";
|
|
41
41
|
import {
|
|
42
42
|
getConfigDir,
|
|
@@ -7,12 +7,13 @@ import {
|
|
|
7
7
|
loadAeroSystemPrompt,
|
|
8
8
|
missingProviderMessage,
|
|
9
9
|
resolveAeroModel,
|
|
10
|
-
resolveAeroSkillDir,
|
|
11
10
|
resolveConfiguredAgentProvider,
|
|
12
11
|
resolveSessionProviderAndModel
|
|
13
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-GL5HD74G.js";
|
|
14
13
|
import "./chunk-IYMTQHPD.js";
|
|
15
|
-
import
|
|
14
|
+
import {
|
|
15
|
+
resolveAeroSkillDir
|
|
16
|
+
} from "./chunk-JI7FDO7I.js";
|
|
16
17
|
import "./chunk-7OAXEMOF.js";
|
|
17
18
|
import {
|
|
18
19
|
AgentProviders,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonry/canonry",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.79.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Self-hosted AI visibility (AEO) platform: track how ChatGPT, Claude, Gemini, and Perplexity cite your domain, join it with Search Console, GA4, server-side traffic, and paid media, and fix what you find through agent tools (CLI, REST, MCP). Local SQLite.",
|
|
6
6
|
"keywords": [
|