@canonry/canonry 5.34.0 → 6.1.1

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 (39) hide show
  1. package/assets/agent-workspace/skills/aero/references/portfolio-analysis.md +15 -3
  2. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +4 -8
  3. package/assets/assets/{AuditHistoryPanel-Ddg6JIi3.js → AuditHistoryPanel-Dtt7cbYo.js} +1 -1
  4. package/assets/assets/{BacklinksPage-DI9BmaYt.js → BacklinksPage-h25kHk3u.js} +1 -1
  5. package/assets/assets/{HistoryPage-CACIL005.js → HistoryPage-70YWNYBU.js} +1 -1
  6. package/assets/assets/{MeasurementPropertyPage-D_i3t0DT.js → MeasurementPropertyPage-CBPQ3A99.js} +1 -1
  7. package/assets/assets/ProjectPage-Dbbxf8yl.js +8 -0
  8. package/assets/assets/RunsPage-B6LqcDRg.js +1 -0
  9. package/assets/assets/SettingsPage-DNV6Is27.js +1 -0
  10. package/assets/assets/SiteHealthSection-CWEIYXut.js +4 -0
  11. package/assets/assets/TrafficPage-ZZqGgMyL.js +40 -0
  12. package/assets/assets/{TrafficSourceDetailPage-BPqNmuws.js → TrafficSourceDetailPage-_RMyqM86.js} +1 -1
  13. package/assets/assets/{extract-error-message-B7OeWj0x.js → extract-error-message-D-AaTlPU.js} +1 -1
  14. package/assets/assets/index-BCKrocfL.js +86 -0
  15. package/assets/assets/index-C17E5ZcA.css +1 -0
  16. package/assets/assets/{react-sigma_core.esm.min-jiMNkILi.js → react-sigma_core.esm.min-CaRTZKvV.js} +1 -1
  17. package/assets/assets/{v2-overview-adapter-Caj3bHQE.js → v2-overview-adapter-C16fKgpq.js} +1 -1
  18. package/assets/index.html +2 -2
  19. package/dist/{chunk-G2K5KCUY.js → chunk-B5W7HDCG.js} +49 -24
  20. package/dist/{chunk-25H4K7KL.js → chunk-DMU3SFC4.js} +8 -8
  21. package/dist/{chunk-NZCKJMLC.js → chunk-HTTYBQN4.js} +2 -2
  22. package/dist/{chunk-NOIK3FUO.js → chunk-LXHE4DVO.js} +68 -17
  23. package/dist/{chunk-VHPGBX4P.js → chunk-MJ3QINUW.js} +3 -3
  24. package/dist/{chunk-QTETJEHI.js → chunk-PDOMOHOP.js} +6 -3
  25. package/dist/{chunk-AIAVZY6L.js → chunk-QFI47QVP.js} +36 -135
  26. package/dist/cli.js +22 -12
  27. package/dist/{demo-server-UI24OAHX.js → demo-server-VP6CP33U.js} +4 -4
  28. package/dist/index.js +6 -6
  29. package/dist/{intelligence-service-ZKFLGJ23.js → intelligence-service-6F63MYQ5.js} +2 -2
  30. package/dist/mcp.js +4 -4
  31. package/package.json +15 -15
  32. package/assets/assets/ProjectPage-d1djqfh3.js +0 -8
  33. package/assets/assets/RunRow-CjhvvA6g.js +0 -1
  34. package/assets/assets/RunsPage-DuBhw2uc.js +0 -1
  35. package/assets/assets/SettingsPage-MUpfB0HH.js +0 -1
  36. package/assets/assets/SiteHealthSection-6qdXlFcO.js +0 -4
  37. package/assets/assets/TrafficPage--nGSOgqn.js +0 -40
  38. package/assets/assets/index-C4lreGwR.js +0 -86
  39. package/assets/assets/index-DX5M4MHr.css +0 -1
@@ -8062,8 +8062,16 @@ var measurementPortfolioAnswerSourcesSchema = z22.object({
8062
8062
  properties: measurementDemoCountSchema,
8063
8063
  answers: measurementDemoCountSchema,
8064
8064
  domains: z22.array(measurementPortfolioCountedDomainSchema).max(MEASUREMENT_PORTFOLIO_ANSWER_SOURCES_LIMIT),
8065
- domainTotal: measurementDemoCountSchema
8065
+ domainTotal: measurementDemoCountSchema,
8066
+ /**
8067
+ * Answers in the basis that cite any of the project's own domains, each
8068
+ * answer once. Optional only so an older server's response still parses.
8069
+ */
8070
+ ownDomainAnswers: measurementDemoCountSchema.optional()
8066
8071
  }).strict().superRefine((sources, ctx) => {
8072
+ if (sources.ownDomainAnswers !== void 0 && sources.ownDomainAnswers > sources.answers) {
8073
+ ctx.addIssue({ code: z22.ZodIssueCode.custom, path: ["ownDomainAnswers"], message: "Own-domain answers cannot exceed the basis" });
8074
+ }
8067
8075
  if (sources.domains.length > sources.domainTotal) {
8068
8076
  ctx.addIssue({ code: z22.ZodIssueCode.custom, path: ["domainTotal"], message: "Total cannot be smaller than the returned domains" });
8069
8077
  }
@@ -8414,7 +8422,14 @@ var measurementChangedPropertySchema = measurementDemoPropertySchema.extend({
8414
8422
  * whenever the denominator held. False when a metric was measured in one
8415
8423
  * run only.
8416
8424
  */
8417
- withinNoise: z22.boolean().optional()
8425
+ withinNoise: z22.boolean().optional(),
8426
+ /**
8427
+ * The same noise rule for each signal alone: a Property beyond noise overall
8428
+ * can still have one signal that moved two answers or fewer, which is not a
8429
+ * gain or loss on its own. Null when that signal cannot be sized.
8430
+ */
8431
+ mentionWithinNoise: z22.boolean().nullable().optional(),
8432
+ citationWithinNoise: z22.boolean().nullable().optional()
8418
8433
  }).strict();
8419
8434
  var measurementChangesResponseSchema = z22.object({
8420
8435
  current: measurementComparableRunSchema,
@@ -9262,6 +9277,14 @@ function formatDate(iso) {
9262
9277
  return iso;
9263
9278
  }
9264
9279
  }
9280
+ var MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
9281
+ function formatCalendarDay(day, now = /* @__PURE__ */ new Date()) {
9282
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(day);
9283
+ const month = match ? MONTH_NAMES[Number(match[2]) - 1] : void 0;
9284
+ if (!match || !month) return day;
9285
+ const label = `${month} ${Number(match[3])}`;
9286
+ return Number(match[1]) === now.getFullYear() ? label : `${label}, ${match[1]}`;
9287
+ }
9265
9288
  function formatIsoDate(iso) {
9266
9289
  if (!iso) return "\u2014";
9267
9290
  try {
@@ -9640,8 +9663,8 @@ function dayOf(value) {
9640
9663
  function sortedUnique(days) {
9641
9664
  return [...new Set(days.filter((d) => d !== null))].sort();
9642
9665
  }
9643
- function normalizeEngineChange(provider, entry) {
9644
- const engine = answerEngineName(provider);
9666
+ function normalizeEngineChange(provider, entry, engineName) {
9667
+ const engine = engineName(provider);
9645
9668
  const perChange = Array.isArray(entry.changes) ? entry.changes : [];
9646
9669
  const changeCount = countOf(entry.changeCount) || perChange.length;
9647
9670
  const unconfirmedCount = Math.min(
@@ -9677,8 +9700,8 @@ function joinNames(names) {
9677
9700
  }
9678
9701
  function whenPhrase(dates, count2) {
9679
9702
  if (dates.length === 0) return null;
9680
- if (dates.length === 1 && count2 <= 1) return `on ${dates[0]}`;
9681
- return `more than once between ${dates[0]} and ${dates[dates.length - 1]}`;
9703
+ if (dates.length === 1 && count2 <= 1) return `on ${formatCalendarDay(dates[0])}`;
9704
+ return `more than once between ${formatCalendarDay(dates[0])} and ${formatCalendarDay(dates[dates.length - 1])}`;
9682
9705
  }
9683
9706
  function engineClause(change) {
9684
9707
  const confirmedWhen = whenPhrase(change.confirmedDates, change.confirmedCount);
@@ -9700,10 +9723,11 @@ function uncheckedTailSentence(entries) {
9700
9723
  const unchecked = entries.filter((e) => !e.checkedThroughPeriodEnd && e.knownGoodAsOf !== null);
9701
9724
  if (unchecked.length === 0) return null;
9702
9725
  const oldest = unchecked.map((e) => e.knownGoodAsOf).sort()[0];
9703
- return `We last checked for model updates on ${oldest}, and this period runs past that date, so there may be later updates we do not know about.`;
9726
+ return `We last checked for model updates on ${formatCalendarDay(oldest)}, and this period runs past that date, so there may be later updates we do not know about.`;
9704
9727
  }
9705
- function buildModelChangeNotice(disclosures) {
9706
- const entries = Object.entries(disclosures ?? {}).map(([provider, entry]) => normalizeEngineChange(provider, entry ?? {})).sort((a, b) => a.engine.localeCompare(b.engine));
9728
+ function buildModelChangeNotice(disclosures, options = {}) {
9729
+ const engineName = options.engineName ?? answerEngineName;
9730
+ const entries = Object.entries(disclosures ?? {}).map(([provider, entry]) => normalizeEngineChange(provider, entry ?? {}, engineName)).sort((a, b) => a.engine.localeCompare(b.engine));
9707
9731
  if (entries.length === 0) return null;
9708
9732
  const changed = entries.filter((e) => e.confirmedCount + e.unconfirmedCount > 0);
9709
9733
  if (changed.length === 0) {
@@ -9711,7 +9735,7 @@ function buildModelChangeNotice(disclosures) {
9711
9735
  const plural = engines.length > 1;
9712
9736
  const tail2 = uncheckedTailSentence(entries);
9713
9737
  const checked = [...new Set(entries.map((e) => e.knownGoodAsOf).filter((d) => d !== null))].sort();
9714
- const freshness = tail2 ?? (checked.length > 0 ? `We last checked for model updates on ${checked[0]}.` : null);
9738
+ const freshness = tail2 ?? (checked.length > 0 ? `We last checked for model updates on ${formatCalendarDay(checked[0])}.` : null);
9715
9739
  return {
9716
9740
  kind: "no-known-change",
9717
9741
  text: `No model updates are on record for ${joinNames(engines)} in this period.`,
@@ -11720,8 +11744,7 @@ var ProviderIds = {
11720
11744
  muse: "muse",
11721
11745
  local: "local",
11722
11746
  cdpChatgpt: "cdp:chatgpt",
11723
- zai: "zai",
11724
- deepinfra: "deepinfra"
11747
+ zai: "zai"
11725
11748
  };
11726
11749
  var PROVIDER_IDS = Object.values(ProviderIds);
11727
11750
  var SweepProviderIds = {
@@ -11738,13 +11761,16 @@ var AgentProviderIds = {
11738
11761
  claude: ProviderIds.claude,
11739
11762
  openai: ProviderIds.openai,
11740
11763
  gemini: ProviderIds.gemini,
11741
- zai: ProviderIds.zai,
11742
- deepinfra: ProviderIds.deepinfra
11764
+ zai: ProviderIds.zai
11743
11765
  };
11744
11766
  var AGENT_PROVIDER_IDS = Object.values(AgentProviderIds);
11745
11767
  function isAgentProviderId(value) {
11746
11768
  return AGENT_PROVIDER_IDS.includes(value);
11747
11769
  }
11770
+ var RETIRED_AGENT_PROVIDER_IDS = ["deepinfra"];
11771
+ function isRetiredAgentProviderId(value) {
11772
+ return RETIRED_AGENT_PROVIDER_IDS.includes(value);
11773
+ }
11748
11774
  var LlmCapabilities = {
11749
11775
  agent: "agent",
11750
11776
  analyze: "analyze",
@@ -14048,6 +14074,12 @@ var sourceCategoryCountSchema = z43.object({
14048
14074
  });
14049
14075
  var sourceRankEntrySchema = z43.object({
14050
14076
  domain: z43.string(),
14077
+ /**
14078
+ * 1-based position by `count` over the whole scope, before any `limit`.
14079
+ * Domains with equal counts share a rank (competition ranking: 1, 2, 2, 4).
14080
+ * Optional only so an older server's response still parses.
14081
+ */
14082
+ rank: z43.number().int().positive().optional(),
14051
14083
  count: z43.number().int(),
14052
14084
  /** Share of the list's `totalCitedSlots`, 0..1 (at wire precision). */
14053
14085
  percentage: fraction(),
@@ -15318,7 +15350,7 @@ function agentVisibilityEvidence(report, project, retrievedAt) {
15318
15350
  ))
15319
15351
  };
15320
15352
  }
15321
- var agentProviderIdSchema = z49.enum(["claude", "openai", "gemini", "zai", "deepinfra"]);
15353
+ var agentProviderIdSchema = z49.enum(["claude", "openai", "gemini", "zai"]);
15322
15354
  var agentProviderOptionDtoSchema = z49.object({
15323
15355
  /** Stable identifier — what clients pass back as `provider` on the prompt endpoint. */
15324
15356
  id: agentProviderIdSchema,
@@ -15723,7 +15755,19 @@ var sentimentCompareRequestSchema = sentimentSelectionBaseSchema.extend({ fromRu
15723
15755
  });
15724
15756
  var sentimentResolvedSelectionSchema = sentimentSelectionBaseSchema.extend({ runId: id.nullable(), revision: z51.number().int().nullable(), evaluationDefinitionId: id.nullable(), mode: z51.enum(["simple", "advanced"]) });
15725
15757
  var sentimentCountsSchema = z51.object(Object.fromEntries(sentimentOutcomeSchema.options.map((outcome) => [outcome, count]))).strict();
15726
- var sentimentCoverageSchema = z51.object({ selected: count, eligibleAssessments: count, unadmittedAssessments: count, judged: count, distinctSourceAnswers: count, counts: sentimentCountsSchema, expectedProviderSlots: count, completedProviderSlots: count }).strict();
15758
+ var sentimentCoverageSchema = z51.object({
15759
+ selected: count,
15760
+ eligibleAssessments: count,
15761
+ unadmittedAssessments: count,
15762
+ judged: count,
15763
+ distinctSourceAnswers: count,
15764
+ eligibleAnswers: count.optional(),
15765
+ ratedAnswers: count.optional(),
15766
+ ratedAnswerRate: rate.optional(),
15767
+ counts: sentimentCountsSchema,
15768
+ expectedProviderSlots: count,
15769
+ completedProviderSlots: count
15770
+ }).strict();
15727
15771
  var sentimentScoreSchema = z51.object({ favorableRate: rate, mixedRate: rate, unfavorableRate: rate, favorableDisplay: z51.string(), mixedDisplay: z51.string(), unfavorableDisplay: z51.string(), interval: z51.object({ low: z51.number(), high: z51.number() }).strict().nullable(), method: z51.literal("wilson-independent-v1"), limitation: z51.string() }).strict();
15728
15772
  var headlineFields = { state: sentimentStateSchema, reason: z51.string().nullable(), provisional: z51.boolean(), coverage: sentimentCoverageSchema, score: sentimentScoreSchema };
15729
15773
  var sentimentHeadlineSchema = z51.object({ ...headlineFields, selection: sentimentResolvedSelectionSchema, runIds: z51.array(id) }).strict();
@@ -15848,11 +15892,17 @@ function emptySentimentCounts() {
15848
15892
  function sentimentRateDisplay(value) {
15849
15893
  return value === null ? "Unavailable" : formatPercent(value, RatioUnits.fraction);
15850
15894
  }
15895
+ var JUDGED_OUTCOMES = /* @__PURE__ */ new Set(["favorable", "mixed", "unfavorable"]);
15851
15896
  function aggregateSentiment(items, options = {}) {
15852
15897
  const unique = [...new Map(items.map((item) => [item.assessmentId, item])).values()];
15853
15898
  const counts = emptySentimentCounts();
15854
15899
  for (const item of unique) counts[item.outcome]++;
15855
15900
  const judged = counts.favorable + counts.mixed + counts.unfavorable;
15901
+ const distinctSourceAnswers = new Set(unique.map((item) => item.sourceSnapshotId)).size;
15902
+ const ratedInItems = new Set(unique.filter((item) => JUDGED_OUTCOMES.has(item.outcome)).map((item) => item.sourceSnapshotId)).size;
15903
+ const ratedAnswers = Math.max(options.ratedAnswers ?? ratedInItems, ratedInItems);
15904
+ const eligibleAnswers = Math.max(options.eligibleAnswers ?? distinctSourceAnswers, distinctSourceAnswers);
15905
+ const ratedAnswerRate = options.disabled || eligibleAnswers === 0 ? null : roundRatio(ratedAnswers / eligibleAnswers, RatioUnits.fraction);
15856
15906
  const processing = counts.pending + counts.running + counts["waiting-to-retry"];
15857
15907
  const provisional = processing + counts.failed + counts.canceled > 0;
15858
15908
  const state = options.disabled ? "disabled" : unique.length === 0 ? "not-measured" : counts.canceled === unique.length ? "canceled" : counts.failed === unique.length ? "failed" : processing === unique.length ? "processing" : provisional ? "partial" : "complete";
@@ -15862,7 +15912,7 @@ function aggregateSentiment(items, options = {}) {
15862
15912
  return {
15863
15913
  state,
15864
15914
  provisional,
15865
- coverage: { selected: unique.length, eligibleAssessments: Math.max(options.eligibleAssessments ?? unique.length, unique.length), unadmittedAssessments: Math.max(0, (options.eligibleAssessments ?? unique.length) - unique.length), judged, distinctSourceAnswers: new Set(unique.map((item) => item.sourceSnapshotId)).size, counts, expectedProviderSlots: options.expectedProviderSlots ?? 0, completedProviderSlots: options.completedProviderSlots ?? 0 },
15915
+ coverage: { selected: unique.length, eligibleAssessments: Math.max(options.eligibleAssessments ?? unique.length, unique.length), unadmittedAssessments: Math.max(0, (options.eligibleAssessments ?? unique.length) - unique.length), judged, distinctSourceAnswers, eligibleAnswers, ratedAnswers, ratedAnswerRate, counts, expectedProviderSlots: options.expectedProviderSlots ?? 0, completedProviderSlots: options.completedProviderSlots ?? 0 },
15866
15916
  score: { favorableRate, mixedRate, unfavorableRate, favorableDisplay: sentimentRateDisplay(favorableRate), mixedDisplay: sentimentRateDisplay(mixedRate), unfavorableDisplay: sentimentRateDisplay(unfavorableRate), interval: options.disabled ? null : wilsonInterval(counts.favorable, judged), method: "wilson-independent-v1", limitation: SENTIMENT_INTERVAL_LIMITATION }
15867
15917
  };
15868
15918
  }
@@ -21694,6 +21744,7 @@ export {
21694
21744
  AgentProviderIds,
21695
21745
  AGENT_PROVIDER_IDS,
21696
21746
  isAgentProviderId,
21747
+ isRetiredAgentProviderId,
21697
21748
  LlmCapabilities,
21698
21749
  LLM_CAPABILITIES,
21699
21750
  providerSummaryEntryDtoSchema,
@@ -4,14 +4,14 @@ import {
4
4
  installSkills,
5
5
  loadConfigRaw,
6
6
  saveConfigPatch
7
- } from "./chunk-QTETJEHI.js";
7
+ } from "./chunk-PDOMOHOP.js";
8
8
  import {
9
9
  PACKAGE_VERSION
10
- } from "./chunk-25H4K7KL.js";
10
+ } from "./chunk-DMU3SFC4.js";
11
11
  import {
12
12
  SKILL_MANIFEST_FILENAME,
13
13
  SkillsClients
14
- } from "./chunk-NOIK3FUO.js";
14
+ } from "./chunk-LXHE4DVO.js";
15
15
 
16
16
  // src/skills-autosync.ts
17
17
  import fs from "fs";
@@ -5,7 +5,7 @@ import {
5
5
  PACKAGE_VERSION,
6
6
  canonryMcpTools,
7
7
  isMachineFormat
8
- } from "./chunk-25H4K7KL.js";
8
+ } from "./chunk-DMU3SFC4.js";
9
9
  import {
10
10
  AGENT_PROVIDER_IDS,
11
11
  CANONRY_NPM_PACKAGE_URL,
@@ -28,6 +28,7 @@ import {
28
28
  formatCanonryPluginVersionMismatch,
29
29
  isInstallMethod,
30
30
  isReadOnlyKey,
31
+ isRetiredAgentProviderId,
31
32
  isSensitiveDiagnosticQueryKey,
32
33
  isStrictSemver,
33
34
  normalizeAgentSlug,
@@ -49,7 +50,7 @@ import {
49
50
  skillsClientSchema,
50
51
  upgradeCaveatFor,
51
52
  upgradeCommandFor
52
- } from "./chunk-NOIK3FUO.js";
53
+ } from "./chunk-LXHE4DVO.js";
53
54
 
54
55
  // src/config.ts
55
56
  import fs from "fs";
@@ -429,9 +430,11 @@ Do not use "canonry init --force" for recovery. It replaces credentials without
429
430
  });
430
431
  }
431
432
  if (!agentProviderSchema.safeParse(parsed.agent?.provider).success) {
433
+ const pinned = parsed.agent?.provider;
434
+ const retired = typeof pinned === "string" && isRetiredAgentProviderId(pinned);
432
435
  throw new CliError({
433
436
  code: "CONFIG_INVALID",
434
- message: `Invalid config at ${configPath}: agent.provider must be one of ${AGENT_PROVIDER_IDS.join(", ")}, or left blank.`
437
+ message: retired ? `Invalid config at ${configPath}: agent.provider ${pinned} was removed. Pin one of ${AGENT_PROVIDER_IDS.join(", ")} (with a key under providers.<name>.apiKey), or leave it blank.` : `Invalid config at ${configPath}: agent.provider must be one of ${AGENT_PROVIDER_IDS.join(", ")}, or left blank.`
435
438
  });
436
439
  }
437
440
  if (!agentModelSchema.safeParse(parsed.agent?.model).success) {
@@ -19,21 +19,21 @@ import {
19
19
  runWithUsageTags,
20
20
  saveConfigPatch,
21
21
  sentimentInstallReadiness
22
- } from "./chunk-QTETJEHI.js";
22
+ } from "./chunk-PDOMOHOP.js";
23
23
  import {
24
24
  computeFactorAverages,
25
25
  deleteSiteCrawlGraphLayout,
26
26
  persistSiteCrawlGraphLayout,
27
27
  prepareSiteCrawlGraphLayout,
28
28
  recordedSharePct
29
- } from "./chunk-NZCKJMLC.js";
29
+ } from "./chunk-HTTYBQN4.js";
30
30
  import {
31
31
  CanonryMcpToolNames,
32
32
  CliError,
33
33
  PACKAGE_VERSION,
34
34
  canonryMcpTools,
35
35
  isMachineFormat
36
- } from "./chunk-25H4K7KL.js";
36
+ } from "./chunk-DMU3SFC4.js";
37
37
  import {
38
38
  CC_CACHE_DIR,
39
39
  DUCKDB_SPEC,
@@ -214,7 +214,7 @@ import {
214
214
  siteLivenessState,
215
215
  toAlertView,
216
216
  usageCounters
217
- } from "./chunk-G2K5KCUY.js";
217
+ } from "./chunk-B5W7HDCG.js";
218
218
  import {
219
219
  AGENT_MEMORY_VALUE_MAX_BYTES,
220
220
  AGENT_NONE,
@@ -414,7 +414,7 @@ import {
414
414
  visibilityReportRequestSchema,
415
415
  winnabilityClassLabel,
416
416
  withRetry
417
- } from "./chunk-NOIK3FUO.js";
417
+ } from "./chunk-LXHE4DVO.js";
418
418
 
419
419
  // src/runtime-context.ts
420
420
  function cliRuntimeContext(env = process.env, stdio = process) {
@@ -7830,7 +7830,7 @@ function nonBlank(value) {
7830
7830
  return trimmed ? trimmed : void 0;
7831
7831
  }
7832
7832
  function resolveBuildCommit(env = process.env) {
7833
- const embedded = true ? "728d0070fa40d19f62082c5a4b31649a57ca2dc7" : void 0;
7833
+ const embedded = true ? "fa1be433951042396fa94c96898efadbb4e1d212" : void 0;
7834
7834
  return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
7835
7835
  }
7836
7836
  function resolveInstanceIdentity(env = process.env) {
@@ -14454,7 +14454,7 @@ function readStoredGroundingSources(rawResponse) {
14454
14454
  return result2;
14455
14455
  }
14456
14456
  async function backfillInsightsCommand(project, opts) {
14457
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-ZKFLGJ23.js");
14457
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-6F63MYQ5.js");
14458
14458
  const config = loadConfig();
14459
14459
  const db = createClient(config.database);
14460
14460
  migrate(db);
@@ -16687,22 +16687,23 @@ import { Agent } from "@earendil-works/pi-agent-core";
16687
16687
  import "@earendil-works/pi-ai";
16688
16688
 
16689
16689
  // src/agent/pi-models.ts
16690
- import {
16691
- createProvider,
16692
- envApiKeyAuth
16693
- } from "@earendil-works/pi-ai";
16690
+ import "@earendil-works/pi-ai";
16694
16691
  import { builtinModels } from "@earendil-works/pi-ai/providers/all";
16695
- import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
16696
16692
  var aeroModels = builtinModels();
16697
- aeroModels.setProvider(createProvider({
16698
- id: "deepinfra",
16699
- auth: { apiKey: envApiKeyAuth("DeepInfra", ["DEEPINFRA_TOKEN"]) },
16700
- models: [],
16701
- api: openAICompletionsApi()
16702
- }));
16703
16693
  var DEFAULT_MAX_RETRIES = 2;
16704
16694
  var DEFAULT_MAX_OUTPUT_TOKENS = 32e3;
16705
16695
  var CANONRY_USER_AGENT = "canonry";
16696
+ var STAINLESS_HOST_HEADERS = {
16697
+ "X-Stainless-OS": null,
16698
+ "X-Stainless-Arch": null,
16699
+ "X-Stainless-Runtime-Version": null
16700
+ };
16701
+ var HOST_HEADERS = {
16702
+ "anthropic-messages": STAINLESS_HOST_HEADERS,
16703
+ "openai-responses": STAINLESS_HOST_HEADERS,
16704
+ "openai-completions": STAINLESS_HOST_HEADERS,
16705
+ "google-generative-ai": { "x-goog-api-client": CANONRY_USER_AGENT }
16706
+ };
16706
16707
  function outputCap(model) {
16707
16708
  return Math.min(model.maxTokens, DEFAULT_MAX_OUTPUT_TOKENS);
16708
16709
  }
@@ -16711,7 +16712,7 @@ function canonryDefaults(model, options) {
16711
16712
  ...options,
16712
16713
  maxRetries: options?.maxRetries ?? DEFAULT_MAX_RETRIES,
16713
16714
  maxTokens: options?.maxTokens ?? outputCap(model),
16714
- headers: { "User-Agent": CANONRY_USER_AGENT, ...options?.headers }
16715
+ headers: { "User-Agent": CANONRY_USER_AGENT, ...HOST_HEADERS[model.api], ...options?.headers }
16715
16716
  };
16716
16717
  }
16717
16718
  var aeroStreamFn = (model, context, options) => aeroModels.streamSimple(model, context, canonryDefaults(model, options));
@@ -16746,22 +16747,6 @@ var PROVIDER_MODELS = {
16746
16747
  [LlmCapabilities.agent]: "glm-5.2",
16747
16748
  [LlmCapabilities.analyze]: "glm-5-turbo",
16748
16749
  [LlmCapabilities.classify]: "glm-5-turbo"
16749
- },
16750
- [AgentProviderIds.deepinfra]: {
16751
- // DeepInfra `org/Model` slugs, resolved into a custom openai-completions
16752
- // model rather than pi-ai's catalog.
16753
- //
16754
- // The agent tier is pinned explicitly: the dashboard picker sends a
16755
- // provider and no model, so whatever sits here is what selecting DeepInfra
16756
- // pins a session to.
16757
- //
16758
- // analyze + classify stay on GLM on purpose. Those tiers suppress the
16759
- // reasoning trace through GLM's chat-template switch, and that branch
16760
- // applies only to a model declared `reasoning: true`; moving them to
16761
- // DeepSeek-V4-Flash (which is not) would quietly stop suppressing anything.
16762
- [LlmCapabilities.agent]: "deepseek-ai/DeepSeek-V4-Flash",
16763
- [LlmCapabilities.analyze]: "zai-org/GLM-5.2",
16764
- [LlmCapabilities.classify]: "zai-org/GLM-5.2"
16765
16750
  }
16766
16751
  };
16767
16752
  var AGENT_PROVIDERS = {
@@ -16788,72 +16773,6 @@ var AGENT_PROVIDERS = {
16788
16773
  label: "Z.ai (GLM)",
16789
16774
  defaultModel: PROVIDER_MODELS[AgentProviderIds.zai][LlmCapabilities.agent],
16790
16775
  autoDetectPriority: 3
16791
- },
16792
- [AgentProviderIds.deepinfra]: {
16793
- // `piAiProvider` is the canonical id here (not a pi-ai vendor) — it's the
16794
- // `model.provider` string the agent loop hands to `getApiKey`, which
16795
- // `resolveAgentId` maps straight back to the deepinfra config/env key.
16796
- piAiProvider: AgentProviderIds.deepinfra,
16797
- label: "DeepInfra (GLM / DeepSeek)",
16798
- defaultModel: PROVIDER_MODELS[AgentProviderIds.deepinfra][LlmCapabilities.agent],
16799
- autoDetectPriority: 4,
16800
- openaiCompatible: {
16801
- baseUrl: "https://api.deepinfra.com/v1/openai",
16802
- baseUrlEnvVar: "DEEPINFRA_BASE_URL",
16803
- apiKeyEnvVar: "DEEPINFRA_TOKEN",
16804
- // DeepInfra serves open-weight models (GLM, DeepSeek) behind an
16805
- // OpenAI-compatible vLLM endpoint. pi-ai's `detectCompat` has no rule
16806
- // for api.deepinfra.com, so it would apply the strict OpenAI profile;
16807
- // we set the same open-model profile pi-ai uses for deepseek.com /
16808
- // cerebras / z.ai. `developer` role and `reasoning_effort` are
16809
- // OpenAI-platform-isms these models reject, `store` is response
16810
- // persistence DeepInfra doesn't implement, and DeepInfra documents
16811
- // `max_tokens` (not `max_completion_tokens`) on its OpenAI route.
16812
- compat: {
16813
- supportsStore: false,
16814
- supportsDeveloperRole: false,
16815
- supportsReasoningEffort: false,
16816
- maxTokensField: "max_tokens"
16817
- },
16818
- // Best-effort metadata. Costs are USD/1M tokens (DeepInfra published
16819
- // rates: GLM-5.2 ~$0.95 in / $0.18 cached / $3.00 out; DeepSeek-V4-Flash
16820
- // $0.09 in / $0.018 cached (0.2x input) / $0.18 out; DeepSeek-V4-Flash-0731
16821
- // $0.06 in / $0.015 cached (0.25x input) / $0.18 out). cacheRead must carry
16822
- // the cached rate, or cached input tokens are logged as free.
16823
- // contextWindow is DeepInfra's 1M (fp4) serving window for these models;
16824
- // it's descriptive (see OpenAiCompatibleModelMeta), so it documents the
16825
- // real window rather than gating compaction.
16826
- knownModels: {
16827
- "zai-org/GLM-5.2": {
16828
- contextWindow: 1048576,
16829
- maxTokens: 98304,
16830
- reasoning: true,
16831
- cost: { input: 0.95, output: 3, cacheRead: 0.18, cacheWrite: 0 }
16832
- },
16833
- "deepseek-ai/DeepSeek-V4-Flash": {
16834
- contextWindow: 1048576,
16835
- maxTokens: 32768,
16836
- reasoning: false,
16837
- cost: { input: 0.09, output: 0.18, cacheRead: 0.018, cacheWrite: 0 }
16838
- },
16839
- "deepseek-ai/DeepSeek-V4-Flash-0731": {
16840
- contextWindow: 1048576,
16841
- maxTokens: 32768,
16842
- reasoning: false,
16843
- cost: { input: 0.06, output: 0.18, cacheRead: 0.015, cacheWrite: 0 }
16844
- }
16845
- },
16846
- // Fallback for arbitrary `--model` slugs we don't ship as tiers. cost is
16847
- // 0, so an unknown slug isn't cost-tracked by pi-ai's `calculateCost`;
16848
- // contextWindow is 0 (unknown) because a guessed window would clamp a long
16849
- // turn's output cap toward 1 token; the host enforces its real window.
16850
- defaultModelMeta: {
16851
- contextWindow: 0,
16852
- maxTokens: 32768,
16853
- reasoning: false,
16854
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
16855
- }
16856
- }
16857
16776
  }
16858
16777
  };
16859
16778
  function agentProvidersByPriority() {
@@ -16875,10 +16794,6 @@ function resolveModelForProvider(provider, modelId) {
16875
16794
  function resolveModelForCapability(provider, capability, modelIdOverride) {
16876
16795
  const entry = AGENT_PROVIDERS[provider];
16877
16796
  const id = modelIdOverride ?? PROVIDER_MODELS[provider][capability];
16878
- if (entry.openaiCompatible) {
16879
- const suppressThinking = capability !== LlmCapabilities.agent;
16880
- return buildOpenAiCompatibleModel(entry, id, suppressThinking);
16881
- }
16882
16797
  const model = aeroModels.getModel(entry.piAiProvider, id);
16883
16798
  if (!model) {
16884
16799
  throw new Error(
@@ -16892,31 +16807,8 @@ function resolveModelForCapability(provider, capability, modelIdOverride) {
16892
16807
  }
16893
16808
  return model;
16894
16809
  }
16895
- function buildOpenAiCompatibleModel(entry, id, suppressThinking = false) {
16896
- const host = entry.openaiCompatible;
16897
- if (!host) {
16898
- throw new Error(`buildOpenAiCompatibleModel called for non-custom provider '${entry.piAiProvider}'`);
16899
- }
16900
- const meta = host.knownModels[id] ?? host.defaultModelMeta;
16901
- const baseUrl = (host.baseUrlEnvVar ? process.env[host.baseUrlEnvVar] : void 0) || host.baseUrl;
16902
- const compat = suppressThinking ? { ...host.compat, thinkingFormat: "chat-template", chatTemplateKwargs: { enable_thinking: { $var: "thinking.enabled" } } } : host.compat;
16903
- return {
16904
- id,
16905
- name: id,
16906
- api: "openai-completions",
16907
- provider: entry.piAiProvider,
16908
- baseUrl,
16909
- reasoning: meta.reasoning,
16910
- input: ["text"],
16911
- cost: meta.cost,
16912
- contextWindow: meta.contextWindow,
16913
- maxTokens: meta.maxTokens,
16914
- ...compat ? { compat } : {}
16915
- };
16916
- }
16917
16810
  function isAgentModelAvailable(provider, id) {
16918
- const entry = AGENT_PROVIDERS[provider];
16919
- return !!entry.openaiCompatible || !!aeroModels.getModel(entry.piAiProvider, id);
16811
+ return !!aeroModels.getModel(AGENT_PROVIDERS[provider].piAiProvider, id);
16920
16812
  }
16921
16813
  function validateAgentProviderRegistry() {
16922
16814
  for (const provider of listAgentProviders()) {
@@ -16952,7 +16844,6 @@ var CATALOG_API_KEY_ENV_VARS = {
16952
16844
  };
16953
16845
  function agentProviderApiKeyEnvVars(id) {
16954
16846
  const entry = AGENT_PROVIDERS[id];
16955
- if (entry.openaiCompatible) return [entry.openaiCompatible.apiKeyEnvVar];
16956
16847
  return CATALOG_API_KEY_ENV_VARS[entry.piAiProvider] ?? [`${entry.piAiProvider.toUpperCase()}_API_KEY`];
16957
16848
  }
16958
16849
  function agentProviderApiKeyEnvVar(id) {
@@ -17181,6 +17072,9 @@ function render(schema, value, components, depth) {
17181
17072
  if (rendered !== child) changed = true;
17182
17073
  out[key] = rendered;
17183
17074
  }
17075
+ if (typeof current.value === "number" && typeof out.value === "string" && Number.isInteger(current.numerator) && Number.isInteger(current.denominator)) {
17076
+ out.value = `${out.value} (${current.numerator}/${current.denominator})`;
17077
+ }
17184
17078
  return changed ? out : current;
17185
17079
  }
17186
17080
  return current;
@@ -18409,7 +18303,7 @@ function buildAeroViewTool(options, validatedEvidence) {
18409
18303
  }
18410
18304
  };
18411
18305
  }
18412
- var AERO_RUNTIME_PROMPT = "\n\nNative Aero tools: when present, use aero_list_toolkits and aero_load_toolkit to discover and load task-specific tools before calling them. Otherwise use the exposed catalog. Start view-relative analysis with aero_inspect_view when available. Cite the returned source.href beside claims. Name the measurement date, class, numerator/denominator, and comparison limitations. Separate observations from possible explanations; do not infer causation. Never turn missing, incomplete, or truncated evidence into zero. A tool result that says it was truncated is partial: never list, rank, count or group items you did not see; say what was cut. Without current view context, resolve ambiguous references before making scoped claims.";
18306
+ var AERO_RUNTIME_PROMPT = "\n\nNative Aero tools: when present, use aero_list_toolkits and aero_load_toolkit to discover and load task-specific tools before calling them. Otherwise use the exposed catalog. Start view-relative analysis with aero_inspect_view when available. Cite the returned source.href beside claims. Name the measurement date, class, numerator/denominator, and comparison limitations. Separate observations from possible explanations; do not infer causation. Never turn missing, incomplete, or truncated evidence into zero. A tool result that says it was truncated is partial: never list, rank, count or group items you did not see; say what was cut. Claim a rank or superlative (first, second, top, most, largest, worst) only from a rank field or a list you saw in full. Without current view context, resolve ambiguous references before making scoped claims. Reply with the final answer only: no plan, no narration of what you will do next, and no draft before it.";
18413
18307
  function aeroViewPrompt(context) {
18414
18308
  return context ? `
18415
18309
 
@@ -18671,6 +18565,9 @@ async function connectStreamableHttp(server) {
18671
18565
  function isReadOnly(tool) {
18672
18566
  return tool.annotations?.readOnlyHint === true;
18673
18567
  }
18568
+ function isRemoteToolError(result2) {
18569
+ return typeof result2 === "object" && result2 !== null && "isError" in result2 && result2.isError === true;
18570
+ }
18674
18571
  function adaptRemoteTool(client, tool) {
18675
18572
  const parameters = Type6.Unsafe(
18676
18573
  tool.inputSchema ?? { type: "object", properties: {} }
@@ -18679,7 +18576,8 @@ function adaptRemoteTool(client, tool) {
18679
18576
  const result2 = await client.callTool({ name: tool.name, arguments: params });
18680
18577
  return {
18681
18578
  content: [{ type: "text", text: truncate(JSON.stringify(result2, null, 2)) }],
18682
- details: result2
18579
+ details: result2,
18580
+ isError: isRemoteToolError(result2)
18683
18581
  };
18684
18582
  };
18685
18583
  return {
@@ -18883,7 +18781,7 @@ Project shape: an Advanced Measurement plan on the legacy schema v1 (plan revisi
18883
18781
  prompt: `
18884
18782
 
18885
18783
  Project shape: an Advanced Measurement portfolio (plan revision ${version.revision}) with ${size}, ${queriesIn("branded")} branded and ${queriesIn("non-brand")} non-brand queries. This line settles the project type.
18886
- Rules: measure per Property, group or market, never pool branded and non-brand, and a query shared by several Properties is one answer scored for each. Denominators count answers (queries x engines), not queries; label them answers. Group Properties only by the metro and submarkets the tools return, never by name. Names given instead were written in the answer text, not cited; cited domains are sources and never go under a named-instead heading. Name only engines a tool returned. Between two sweeps, a Property that moved ${MEASUREMENT_CHANGES_NOISE_ANSWERS} answers or fewer is within noise (withinNoise): never call it a gain, loss, trend or regression. Properties tied at the weakest rate are not ranked: they are listed by name, so say how many tie (tiedAtWeakest.count), give tiedAtWeakest.byMetro, and call the listed rows examples of the tie, not a ranked bottom list. A result with truncated true, a total above its rows, __partialLists or __truncation is partial: say how many of how many you saw, and never call those rows the biggest, all, or the full picture.
18784
+ Rules: measure per Property, group or market, never pool branded and non-brand, and a query shared by several Properties is one answer scored for each. Denominators count answers (queries x engines), not queries; label them answers. Group Properties only by the metro and submarkets the tools return, never by name. Names given instead were written in the answer text, not cited; cited domains are sources and never go under a named-instead heading. Name only engines a tool returned. Between two sweeps, a Property that moved ${MEASUREMENT_CHANGES_NOISE_ANSWERS} answers or fewer is within noise (withinNoise): never call it a gain, loss, trend or regression. Judge mention and citation separately (mentionWithinNoise, citationWithinNoise): a signal that moved ${MEASUREMENT_CHANGES_NOISE_ANSWERS} answers or fewer is within noise even when the Property's other signal moved more. Quote every coverage figure with its counts, headline and branded figures included; the value text carries them, as in 25.0% (6/24). A Property's citation coverage counts only citations of its own page: 0% does not mean your domain went uncited (weakestAnswerSources.ownDomainAnswers counts that). Property names often carry the brand, so that most listed Properties carry it is not a finding. Properties tied at the weakest rate are not ranked: they are listed by name, so say how many tie (tiedAtWeakest.count), give tiedAtWeakest.byMetro, and call the listed rows examples of the tie, not a ranked bottom list. A result with truncated true, a total above its rows, __partialLists or __truncation is partial: say how many of how many you saw, and never call those rows the biggest, all, or the full picture.
18887
18785
  Route each question to its read:
18888
18786
  ${ADVANCED_ROUTES.map((route) => `- ${route}`).join("\n")}
18889
18787
  canonry_visibility_report describes the whole project, not one Property. canonry_measurement_plan_get is plan structure with no metrics; do not read it for analysis.`,
@@ -19420,7 +19318,9 @@ ${lines.join("\n")}
19420
19318
  */
19421
19319
  resolveTurnModel(preferences, current) {
19422
19320
  const pinned = resolveConfiguredAgentProvider(this.opts.config);
19423
- const provider = preferences?.provider ?? pinned ?? current.provider ?? AgentProviderIds.claude;
19321
+ const stored = coerceAgentProvider(current.provider ?? void 0);
19322
+ const provider = preferences?.provider ?? pinned ?? stored ?? detectAgentProvider(this.opts.config);
19323
+ if (!provider) throw new Error(missingProviderMessage());
19424
19324
  const carried = !preferences?.provider && !pinned && provider === current.provider && current.modelId && isAgentModelAvailable(provider, current.modelId) ? current.modelId : void 0;
19425
19325
  const pinnedModelId = provider === pinned ? this.opts.config.agent?.model ?? void 0 : void 0;
19426
19326
  const modelId = preferences?.modelId ?? pinnedModelId ?? carried ?? getAgentProvider(provider).defaultModel;
@@ -19592,6 +19492,7 @@ ${lines.join("\n")}
19592
19492
  });
19593
19493
  return;
19594
19494
  }
19495
+ this.resolveTurnModel(void 0, { provider: row.modelProvider, modelId: row.modelId });
19595
19496
  const existing = parseJsonColumn(row.followUpQueue, []);
19596
19497
  this.updateRow(projectId, { followUpQueue: JSON.stringify([...existing, message]) });
19597
19498
  }
@@ -20069,7 +19970,7 @@ function viewerAeroCaller(request, opts) {
20069
19970
  const principal = request.principal;
20070
19971
  return principal?.kind === "user" && principal.role === UserRoles.viewer ? principal.id : null;
20071
19972
  }
20072
- var VIEWER_HIDDEN_ASSISTANT_KEYS = ["api", "provider", "model", "responseId", "usage", "responseModel", "diagnostics", "providerThinkingLevel", "rawStopReason"];
19973
+ var VIEWER_HIDDEN_ASSISTANT_KEYS = ["api", "provider", "model", "responseId", "usage", "responseModel", "diagnostics", "providerThinkingLevel", "thinkingLevel", "rawStopReason"];
20073
19974
  var VIEWER_ERROR_MESSAGE = "Aero could not finish this answer. Try again, or ask an administrator if it keeps happening.";
20074
19975
  function viewerRedact(_key, value) {
20075
19976
  if (!value || typeof value !== "object" || value.role !== "assistant") return value;