@ainyc/canonry 4.71.1 → 4.72.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/regression-playbook.md +1 -1
- package/assets/agent-workspace/skills/canonry/references/aeo-analysis.md +7 -0
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +20 -2
- package/assets/assets/{BacklinksPage-CQNPYiDA.js → BacklinksPage-CjfpwZEH.js} +1 -1
- package/assets/assets/{ChartPrimitives-BShpLrpS.js → ChartPrimitives-Ckf2FrUy.js} +1 -1
- package/assets/assets/{ProjectPage-CJLw1m4O.js → ProjectPage-DZeplYeC.js} +6 -6
- package/assets/assets/{RunRow-Dq1vs1hA.js → RunRow-BuFyG0V_.js} +1 -1
- package/assets/assets/{RunsPage-CBMa2xWh.js → RunsPage-D-pr000K.js} +1 -1
- package/assets/assets/{SettingsPage-B_XeJDdg.js → SettingsPage-CiaapCYn.js} +1 -1
- package/assets/assets/{TrafficPage-vJv_Mf6f.js → TrafficPage-B40xytJD.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-C3yFwVmQ.js → TrafficSourceDetailPage-7hHem-gM.js} +1 -1
- package/assets/assets/{extract-error-message-CIpeBFLl.js → extract-error-message-3GkDsu1h.js} +1 -1
- package/assets/assets/{index-BXLM3-cs.js → index-BVdH2O9w.js} +77 -77
- package/assets/assets/{server-traffic-Yt3jIi3g.js → server-traffic-CsgPsudZ.js} +1 -1
- package/assets/assets/{trash-2-xGvNHhEj.js → trash-2-B8Ipf9rI.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-ZNWMVYYU.js → chunk-NYZSY5QJ.js} +126 -7
- package/dist/{chunk-5FM7QRYD.js → chunk-SJI6JGPN.js} +1249 -1005
- package/dist/{chunk-ETJDAMGA.js → chunk-XYX447L2.js} +613 -87
- package/dist/{chunk-CWEV3YMZ.js → chunk-ZISXWFQA.js} +92 -4
- package/dist/cli.js +306 -84
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-ISO4VGEC.js → intelligence-service-YOZOOYUI.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +8 -8
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
loadConfig,
|
|
10
10
|
loadConfigRaw,
|
|
11
11
|
saveConfigPatch
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-NYZSY5QJ.js";
|
|
13
13
|
import {
|
|
14
14
|
CC_CACHE_DIR,
|
|
15
15
|
DUCKDB_SPEC,
|
|
@@ -95,7 +95,7 @@ import {
|
|
|
95
95
|
runs,
|
|
96
96
|
schedules,
|
|
97
97
|
usageCounters
|
|
98
|
-
} from "./chunk-
|
|
98
|
+
} from "./chunk-XYX447L2.js";
|
|
99
99
|
import {
|
|
100
100
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
101
101
|
AGENT_PROVIDER_IDS,
|
|
@@ -129,6 +129,7 @@ import {
|
|
|
129
129
|
classifySkillFile,
|
|
130
130
|
coerceSkillManifest,
|
|
131
131
|
contentActionLabel,
|
|
132
|
+
contentBriefDtoSchema,
|
|
132
133
|
determineAnswerMentioned,
|
|
133
134
|
effectiveBrandNames,
|
|
134
135
|
effectiveDomains,
|
|
@@ -142,8 +143,9 @@ import {
|
|
|
142
143
|
serializeRunError,
|
|
143
144
|
skillsClientSchema,
|
|
144
145
|
validationError,
|
|
146
|
+
winnabilityClassLabel,
|
|
145
147
|
withRetry
|
|
146
|
-
} from "./chunk-
|
|
148
|
+
} from "./chunk-SJI6JGPN.js";
|
|
147
149
|
|
|
148
150
|
// src/telemetry.ts
|
|
149
151
|
import crypto from "crypto";
|
|
@@ -5618,7 +5620,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
5618
5620
|
return result;
|
|
5619
5621
|
}
|
|
5620
5622
|
async function backfillInsightsCommand(project, opts) {
|
|
5621
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
5623
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-YOZOOYUI.js");
|
|
5622
5624
|
const config = loadConfig();
|
|
5623
5625
|
const db = createClient(config.database);
|
|
5624
5626
|
migrate(db);
|
|
@@ -8455,6 +8457,89 @@ function createRecommendationExplainer(opts) {
|
|
|
8455
8457
|
};
|
|
8456
8458
|
};
|
|
8457
8459
|
}
|
|
8460
|
+
var RECOMMENDATION_BRIEF_PROMPT_VERSION = "v1";
|
|
8461
|
+
var BRIEF_SYSTEM_PROMPT = `You are an AEO (Answer Engine Optimization) analyst writing a content brief for a single winnable query. Your audience is the site owner or their agency.
|
|
8462
|
+
|
|
8463
|
+
Return ONLY a single JSON object \u2014 no prose, no markdown, no code fences \u2014 with EXACTLY these keys, each a non-empty string:
|
|
8464
|
+
- "angle": the differentiated content angle to take (what makes this piece win, not generic advice).
|
|
8465
|
+
- "whyWinnable": why this query is winnable for a first-party page, citing the cited-surface signal from the context (competitors vs aggregators, demand, absence).
|
|
8466
|
+
- "schemaHookup": the concrete schema.org type or markup to add or extend (e.g. "FAQPage", "Product + Review", "HowTo").
|
|
8467
|
+
- "controllableSurfaceRationale": why this cited surface is controllable rather than ceded to aggregators/editorial.
|
|
8468
|
+
|
|
8469
|
+
Do not invent facts beyond the supplied context. Be specific and dense.`;
|
|
8470
|
+
function buildBriefPrompt(input) {
|
|
8471
|
+
const base = buildRecommendationPrompt(input);
|
|
8472
|
+
const r = input.recommendation;
|
|
8473
|
+
const winnability = r.winnability === null ? "unknown (no classification coverage)" : r.winnability.toFixed(2);
|
|
8474
|
+
return [
|
|
8475
|
+
base,
|
|
8476
|
+
`Surface class: ${winnabilityClassLabel(r.winnabilityClass).toLowerCase()} (the cited surface is ${r.winnabilityClass === "ceded" ? "dominated by aggregators/editorial" : "controllable"})`,
|
|
8477
|
+
`Winnability: ${winnability}`
|
|
8478
|
+
].join("\n");
|
|
8479
|
+
}
|
|
8480
|
+
function parseBrief(text, recommendation) {
|
|
8481
|
+
const stripped = text.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "").trim();
|
|
8482
|
+
let parsed;
|
|
8483
|
+
try {
|
|
8484
|
+
parsed = JSON.parse(stripped);
|
|
8485
|
+
} catch {
|
|
8486
|
+
return null;
|
|
8487
|
+
}
|
|
8488
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
8489
|
+
const p = parsed;
|
|
8490
|
+
const candidate = {
|
|
8491
|
+
// Deterministic — injected from the recommendation, not the model.
|
|
8492
|
+
targetQuery: recommendation.query,
|
|
8493
|
+
winnabilityClass: recommendation.winnabilityClass,
|
|
8494
|
+
// Creative — taken from the model reply.
|
|
8495
|
+
angle: p.angle,
|
|
8496
|
+
whyWinnable: p.whyWinnable,
|
|
8497
|
+
schemaHookup: p.schemaHookup,
|
|
8498
|
+
controllableSurfaceRationale: p.controllableSurfaceRationale
|
|
8499
|
+
};
|
|
8500
|
+
const result = contentBriefDtoSchema.safeParse(candidate);
|
|
8501
|
+
return result.success ? result.data : null;
|
|
8502
|
+
}
|
|
8503
|
+
function createRecommendationBriefSynthesizer(opts) {
|
|
8504
|
+
return async (input) => {
|
|
8505
|
+
const provider = pickExplainProvider(opts.config, input.providerOverride);
|
|
8506
|
+
const model = resolveModelForCapability(provider, "analyze", input.modelOverride);
|
|
8507
|
+
const apiKey = resolveApiKeyFor(provider, opts.config);
|
|
8508
|
+
const prompt = buildBriefPrompt({
|
|
8509
|
+
projectName: input.projectName,
|
|
8510
|
+
canonicalDomain: input.canonicalDomain,
|
|
8511
|
+
recommendation: input.recommendation
|
|
8512
|
+
});
|
|
8513
|
+
const MAX_ATTEMPTS = 2;
|
|
8514
|
+
let totalCostDollars = 0;
|
|
8515
|
+
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
|
|
8516
|
+
const userContent = attempt === 0 ? prompt : `${prompt}
|
|
8517
|
+
|
|
8518
|
+
Your previous reply was not valid JSON. Return ONLY a JSON object with exactly the keys: angle, whyWinnable, schemaHookup, controllableSurfaceRationale. No prose, no markdown fences.`;
|
|
8519
|
+
const context = {
|
|
8520
|
+
systemPrompt: BRIEF_SYSTEM_PROMPT,
|
|
8521
|
+
messages: [{ role: "user", content: userContent, timestamp: Date.now() }]
|
|
8522
|
+
};
|
|
8523
|
+
const resp = await complete2(model, context, apiKey ? { apiKey } : {});
|
|
8524
|
+
totalCostDollars += Number.isFinite(resp.usage.cost.total) ? resp.usage.cost.total : 0;
|
|
8525
|
+
const parts = resp.content.filter((p) => p.type === "text");
|
|
8526
|
+
const text = parts.map((p) => p.text).join("\n").trim();
|
|
8527
|
+
const brief = parseBrief(text, input.recommendation);
|
|
8528
|
+
if (brief) {
|
|
8529
|
+
return {
|
|
8530
|
+
promptVersion: RECOMMENDATION_BRIEF_PROMPT_VERSION,
|
|
8531
|
+
provider,
|
|
8532
|
+
model: model.id,
|
|
8533
|
+
brief,
|
|
8534
|
+
costMillicents: dollarsToMillicents(totalCostDollars)
|
|
8535
|
+
};
|
|
8536
|
+
}
|
|
8537
|
+
}
|
|
8538
|
+
throw providerError(
|
|
8539
|
+
`Provider '${provider}' returned unparseable brief output after ${MAX_ATTEMPTS} attempts.`
|
|
8540
|
+
);
|
|
8541
|
+
};
|
|
8542
|
+
}
|
|
8458
8543
|
|
|
8459
8544
|
// src/snapshot-service.ts
|
|
8460
8545
|
import { runAeoAudit } from "@ainyc/aeo-audit";
|
|
@@ -9820,6 +9905,7 @@ async function createServer(opts) {
|
|
|
9820
9905
|
return value;
|
|
9821
9906
|
};
|
|
9822
9907
|
const explainContentRecommendation = createRecommendationExplainer({ config: opts.config });
|
|
9908
|
+
const briefContentRecommendation = createRecommendationBriefSynthesizer({ config: opts.config });
|
|
9823
9909
|
await app.register(apiRoutes, {
|
|
9824
9910
|
db: opts.db,
|
|
9825
9911
|
routePrefix: apiPrefix,
|
|
@@ -9827,6 +9913,8 @@ async function createServer(opts) {
|
|
|
9827
9913
|
sessionCookieName: SESSION_COOKIE_NAME,
|
|
9828
9914
|
resolveSessionApiKeyId,
|
|
9829
9915
|
explainContentRecommendation,
|
|
9916
|
+
briefContentRecommendation,
|
|
9917
|
+
briefPromptVersion: RECOMMENDATION_BRIEF_PROMPT_VERSION,
|
|
9830
9918
|
// On-disk paths the daemon depends on. The api-routes plugin uses these
|
|
9831
9919
|
// to fail loud (HTTP 503) when the operator wipes the DB or config out
|
|
9832
9920
|
// from under a running serve — SQLite holds the inode open across
|