@canonry/canonry 5.30.2 → 5.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (20) hide show
  1. package/assets/assets/{AuditHistoryPanel-8GPJdEWL.js → AuditHistoryPanel-DW2aeqJP.js} +1 -1
  2. package/assets/assets/{BacklinksPage-8-KPXFtA.js → BacklinksPage-DroPpjd3.js} +1 -1
  3. package/assets/assets/{HistoryPage-CKgF71m5.js → HistoryPage-2o6p-ZDF.js} +1 -1
  4. package/assets/assets/{MeasurementPropertyPage-nfGYHl6C.js → MeasurementPropertyPage-VTGLyVuD.js} +1 -1
  5. package/assets/assets/{ProjectPage-BPtdO-Gp.js → ProjectPage-CXV64SBB.js} +1 -1
  6. package/assets/assets/{RunRow-C3CRRfb4.js → RunRow-CIhSIAoR.js} +1 -1
  7. package/assets/assets/{RunsPage-BvCCjtqM.js → RunsPage-lO9xMTSl.js} +1 -1
  8. package/assets/assets/{SettingsPage-B1QuP6Cv.js → SettingsPage-CBwns7cb.js} +1 -1
  9. package/assets/assets/{SiteHealthSection-DgM3aTd-.js → SiteHealthSection-C372wrfT.js} +3 -3
  10. package/assets/assets/{TrafficPage-DEUDhBXG.js → TrafficPage-DGyS-IjJ.js} +1 -1
  11. package/assets/assets/{TrafficSourceDetailPage-gpgLsGA1.js → TrafficSourceDetailPage-BBaz_ivE.js} +1 -1
  12. package/assets/assets/{extract-error-message-CH4G5kw6.js → extract-error-message-QcHrDOjc.js} +1 -1
  13. package/assets/assets/{index-B2qeAPfR.js → index-CbnQjcJj.js} +3 -3
  14. package/assets/assets/{react-sigma_core.esm.min-CG4hSMQ3.js → react-sigma_core.esm.min-D9tjGma4.js} +1 -1
  15. package/assets/assets/{v2-overview-adapter-QNwqt0UG.js → v2-overview-adapter-CLlJkExr.js} +1 -1
  16. package/assets/index.html +1 -1
  17. package/dist/{chunk-7YRGVXR5.js → chunk-6UO23AKO.js} +147 -64
  18. package/dist/cli.js +1 -1
  19. package/dist/index.js +1 -1
  20. package/package.json +12 -13
@@ -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 ? "0417a856c95c1a04b86db6ede81376a8faa015ca" : void 0;
7833
+ const embedded = true ? "b5129303a169ae8903dbf5febfcb10067f0507a1" : void 0;
7834
7834
  return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
7835
7835
  }
7836
7836
  function resolveInstanceIdentity(env = process.env) {
@@ -9655,9 +9655,9 @@ var JobRunner = class {
9655
9655
  const parent = txDb.select({ projectId: runs.projectId, createdAt: runs.createdAt, error: runs.error }).from(runs).where(eq3(runs.id, input.runId)).get();
9656
9656
  const superseded = input.superseded || parent !== void 0 && newerFullSweep(txDb, { id: input.runId, projectId: parent.projectId, createdAt: parent.createdAt }) !== void 0;
9657
9657
  const state = measurementRunSlotState(txDb, input.runId);
9658
- const complete3 = state.planned && state.readable && state.missing.length === 0 && !state.hasUnboundSnapshot;
9658
+ const complete = state.planned && state.readable && state.missing.length === 0 && !state.hasUnboundSnapshot;
9659
9659
  let justCompleted = false;
9660
- if (complete3) {
9660
+ if (complete) {
9661
9661
  justCompleted = txDb.update(runs).set({ status: "completed", error: null }).where(and3(eq3(runs.id, input.runId), eq3(runs.status, "partial"))).run().changes === 1;
9662
9662
  if (justCompleted && parent) recordSentimentCompletion(tx, { projectId: parent.projectId, runId: input.runId, completionKey: input.fillId, completedAt: finishedAt, fillOrigin: input.fillId });
9663
9663
  } else if (state.readable && !input.fatal) {
@@ -9671,9 +9671,9 @@ var JobRunner = class {
9671
9671
  }
9672
9672
  txDb.update(runs).set({ error: serializeRunError({ providers }) }).where(and3(eq3(runs.id, input.runId), eq3(runs.status, "partial"))).run();
9673
9673
  }
9674
- const status = complete3 ? "completed" : input.filled > 0 ? "partial" : "failed";
9674
+ const status = complete ? "completed" : input.filled > 0 ? "partial" : "failed";
9675
9675
  const reason = input.fatal ?? (superseded ? "Stopped because a newer sweep started; no answers were added behind it." : null) ?? (input.providerErrors.size > 0 ? formatRunErrorOneLine(buildRunErrorFromMessages(input.providerErrors)) : null);
9676
- txDb.update(runFills).set({ status, filled: input.filled, error: complete3 ? null : reason, finishedAt }).where(eq3(runFills.id, input.fillId)).run();
9676
+ txDb.update(runFills).set({ status, filled: input.filled, error: complete ? null : reason, finishedAt }).where(eq3(runFills.id, input.fillId)).run();
9677
9677
  return { justCompleted, superseded };
9678
9678
  });
9679
9679
  }
@@ -16679,14 +16679,45 @@ import { eq as eq24 } from "drizzle-orm";
16679
16679
  // src/agent/session.ts
16680
16680
  import fs8 from "fs";
16681
16681
  import path7 from "path";
16682
- import { Agent } from "@mariozechner/pi-agent-core";
16683
- import { registerBuiltInApiProviders } from "@mariozechner/pi-ai";
16682
+ import { Agent } from "@earendil-works/pi-agent-core";
16684
16683
 
16685
16684
  // src/agent/providers.ts
16685
+ import "@earendil-works/pi-ai";
16686
+
16687
+ // src/agent/pi-models.ts
16686
16688
  import {
16687
- getEnvApiKey,
16688
- getModel
16689
- } from "@mariozechner/pi-ai";
16689
+ createProvider,
16690
+ envApiKeyAuth
16691
+ } from "@earendil-works/pi-ai";
16692
+ import { builtinModels } from "@earendil-works/pi-ai/providers/all";
16693
+ import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
16694
+ var aeroModels = builtinModels();
16695
+ aeroModels.setProvider(createProvider({
16696
+ id: "deepinfra",
16697
+ auth: { apiKey: envApiKeyAuth("DeepInfra", ["DEEPINFRA_TOKEN"]) },
16698
+ models: [],
16699
+ api: openAICompletionsApi()
16700
+ }));
16701
+ var DEFAULT_MAX_RETRIES = 2;
16702
+ var DEFAULT_MAX_OUTPUT_TOKENS = 32e3;
16703
+ var CANONRY_USER_AGENT = "canonry";
16704
+ function outputCap(model) {
16705
+ return Math.min(model.maxTokens, DEFAULT_MAX_OUTPUT_TOKENS);
16706
+ }
16707
+ function canonryDefaults(model, options) {
16708
+ return {
16709
+ ...options,
16710
+ maxRetries: options?.maxRetries ?? DEFAULT_MAX_RETRIES,
16711
+ maxTokens: options?.maxTokens ?? outputCap(model),
16712
+ headers: { "User-Agent": CANONRY_USER_AGENT, ...options?.headers }
16713
+ };
16714
+ }
16715
+ var aeroStreamFn = (model, context, options) => aeroModels.streamSimple(model, context, canonryDefaults(model, options));
16716
+ function completeOnce(model, context, options = {}) {
16717
+ return aeroModels.complete(model, context, canonryDefaults(model, options));
16718
+ }
16719
+
16720
+ // src/agent/providers.ts
16690
16721
  var PROVIDER_MODELS = {
16691
16722
  [AgentProviderIds.claude]: {
16692
16723
  [LlmCapabilities.agent]: "claude-opus-4-7",
@@ -16707,9 +16738,10 @@ var PROVIDER_MODELS = {
16707
16738
  [LlmCapabilities.classify]: "gemini-2.5-flash-lite"
16708
16739
  },
16709
16740
  [AgentProviderIds.zai]: {
16710
- // GLM lineage: 5.1 is the latest agent-class model; 5-turbo is the
16711
- // cheaper tier good for structured tasks and classification.
16712
- [LlmCapabilities.agent]: "glm-5.1",
16741
+ // GLM lineage: 5.2 replaced 5.1, which pi-ai 0.87 dropped from its
16742
+ // catalog; 5-turbo is the cheaper tier good for structured tasks and
16743
+ // classification.
16744
+ [LlmCapabilities.agent]: "glm-5.2",
16713
16745
  [LlmCapabilities.analyze]: "glm-5-turbo",
16714
16746
  [LlmCapabilities.classify]: "glm-5-turbo"
16715
16747
  },
@@ -16811,9 +16843,10 @@ var AGENT_PROVIDERS = {
16811
16843
  },
16812
16844
  // Fallback for arbitrary `--model` slugs we don't ship as tiers. cost is
16813
16845
  // 0, so an unknown slug isn't cost-tracked by pi-ai's `calculateCost`;
16814
- // contextWindow stays conservative since we can't know the slug's window.
16846
+ // contextWindow is 0 (unknown) because a guessed window would clamp a long
16847
+ // turn's output cap toward 1 token; the host enforces its real window.
16815
16848
  defaultModelMeta: {
16816
- contextWindow: 131072,
16849
+ contextWindow: 0,
16817
16850
  maxTokens: 32768,
16818
16851
  reasoning: false,
16819
16852
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
@@ -16844,10 +16877,10 @@ function resolveModelForCapability(provider, capability, modelIdOverride) {
16844
16877
  const suppressThinking = capability !== LlmCapabilities.agent;
16845
16878
  return buildOpenAiCompatibleModel(entry, id, suppressThinking);
16846
16879
  }
16847
- const model = getModel(entry.piAiProvider, id);
16880
+ const model = aeroModels.getModel(entry.piAiProvider, id);
16848
16881
  if (!model) {
16849
16882
  throw new Error(
16850
- `Model '${id}' not found for pi-ai provider '${entry.piAiProvider}'. Verify PROVIDER_MODELS[${provider}][${capability}] against the installed @mariozechner/pi-ai catalog.`
16883
+ `Model '${id}' not found for pi-ai provider '${entry.piAiProvider}'. Verify PROVIDER_MODELS[${provider}][${capability}] against the installed @earendil-works/pi-ai catalog.`
16851
16884
  );
16852
16885
  }
16853
16886
  if (provider === AgentProviderIds.gemini && process.env.GEMINI_BASE_URL) {
@@ -16864,7 +16897,7 @@ function buildOpenAiCompatibleModel(entry, id, suppressThinking = false) {
16864
16897
  }
16865
16898
  const meta = host.knownModels[id] ?? host.defaultModelMeta;
16866
16899
  const baseUrl = (host.baseUrlEnvVar ? process.env[host.baseUrlEnvVar] : void 0) || host.baseUrl;
16867
- const compat = suppressThinking ? { ...host.compat, thinkingFormat: "qwen-chat-template" } : host.compat;
16900
+ const compat = suppressThinking ? { ...host.compat, thinkingFormat: "chat-template", chatTemplateKwargs: { enable_thinking: { $var: "thinking.enabled" } } } : host.compat;
16868
16901
  return {
16869
16902
  id,
16870
16903
  name: id,
@@ -16879,6 +16912,10 @@ function buildOpenAiCompatibleModel(entry, id, suppressThinking = false) {
16879
16912
  ...compat ? { compat } : {}
16880
16913
  };
16881
16914
  }
16915
+ function isAgentModelAvailable(provider, id) {
16916
+ const entry = AGENT_PROVIDERS[provider];
16917
+ return !!entry.openaiCompatible || !!aeroModels.getModel(entry.piAiProvider, id);
16918
+ }
16882
16919
  function validateAgentProviderRegistry() {
16883
16920
  for (const provider of listAgentProviders()) {
16884
16921
  for (const capability of LLM_CAPABILITIES) {
@@ -16899,16 +16936,26 @@ function resolveApiKeyFor(providerOrPiAi, config) {
16899
16936
  function resolveApiKeySource(providerOrPiAi, config) {
16900
16937
  const id = resolveAgentId(providerOrPiAi);
16901
16938
  if (!id) return void 0;
16902
- const entry = AGENT_PROVIDERS[id];
16903
16939
  const fromConfig = config.providers?.[id]?.apiKey;
16904
16940
  if (fromConfig) return { key: fromConfig, source: "config" };
16905
- const fromEnv = entry.openaiCompatible ? process.env[entry.openaiCompatible.apiKeyEnvVar] : getEnvApiKey(entry.piAiProvider);
16941
+ const fromEnv = agentProviderApiKeyEnvVars(id).map((name) => process.env[name]).find((value) => !!value);
16906
16942
  if (fromEnv) return { key: fromEnv, source: "env" };
16907
16943
  return void 0;
16908
16944
  }
16909
- function agentProviderApiKeyEnvVar(id) {
16945
+ var CATALOG_API_KEY_ENV_VARS = {
16946
+ anthropic: ["ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY"],
16947
+ openai: ["OPENAI_API_KEY"],
16948
+ google: ["GEMINI_API_KEY"],
16949
+ zai: ["ZAI_API_KEY"]
16950
+ };
16951
+ function agentProviderApiKeyEnvVars(id) {
16910
16952
  const entry = AGENT_PROVIDERS[id];
16911
- return entry.openaiCompatible ? entry.openaiCompatible.apiKeyEnvVar : `${entry.piAiProvider.toUpperCase()}_API_KEY`;
16953
+ if (entry.openaiCompatible) return [entry.openaiCompatible.apiKeyEnvVar];
16954
+ return CATALOG_API_KEY_ENV_VARS[entry.piAiProvider] ?? [`${entry.piAiProvider.toUpperCase()}_API_KEY`];
16955
+ }
16956
+ function agentProviderApiKeyEnvVar(id) {
16957
+ const names = agentProviderApiKeyEnvVars(id);
16958
+ return names[names.length - 1];
16912
16959
  }
16913
16960
  function resolveAgentId(providerOrPiAi) {
16914
16961
  if (isAgentProviderId(providerOrPiAi)) return providerOrPiAi;
@@ -16959,7 +17006,7 @@ function resolveAeroSkillDir(pkgDir) {
16959
17006
  // src/agent/skill-tools.ts
16960
17007
  import fs7 from "fs";
16961
17008
  import path6 from "path";
16962
- import { Type } from "@sinclair/typebox";
17009
+ import { Type } from "@earendil-works/pi-ai";
16963
17010
  var MAX_DOC_CHARS = 2e4;
16964
17011
  function textResult(details) {
16965
17012
  return {
@@ -17050,10 +17097,10 @@ function buildSkillDocTools() {
17050
17097
  }
17051
17098
 
17052
17099
  // src/agent/tools.ts
17053
- import { Type as Type3 } from "@sinclair/typebox";
17100
+ import { Type as Type3 } from "@earendil-works/pi-ai";
17054
17101
 
17055
17102
  // src/agent/mcp-to-agent-tool.ts
17056
- import { Type as Type2 } from "@sinclair/typebox";
17103
+ import { Type as Type2 } from "@earendil-works/pi-ai";
17057
17104
  import { randomUUID as randomUUID2 } from "crypto";
17058
17105
 
17059
17106
  // src/agent/tool-result-units.ts
@@ -18076,7 +18123,12 @@ function splitAeroAnthropicSystemCachePayload(payload, model) {
18076
18123
 
18077
18124
  // src/agent/runtime.ts
18078
18125
  import { z } from "zod";
18079
- import { Type as Type4 } from "@sinclair/typebox";
18126
+ import {
18127
+ Type as Type4,
18128
+ createInitialSystemMessage,
18129
+ getCurrentTools,
18130
+ toToolDeclaration
18131
+ } from "@earendil-works/pi-ai";
18080
18132
  var DISCOVER = "aero_list_toolkits";
18081
18133
  var MAX_VISIBLE_TOOLS = 128;
18082
18134
  var LOAD = "aero_load_toolkit";
@@ -18209,20 +18261,23 @@ function configureAeroRuntime(agent, allowed, limits, progressive = true, pinned
18209
18261
  runtime = { allowed, loaded: /* @__PURE__ */ new Set(), limits: agentTurnLimitsSchema.parse(limits ?? {}), calls: 0, rounds: 0, startedAt: 0, reason: "completed", progressive, pinned: new Set(pinned), corrected: /* @__PURE__ */ new Map() };
18210
18262
  runtimes.set(agent, runtime);
18211
18263
  const state = runtime;
18212
- const stream = agent.streamFn;
18213
- agent.streamFn = (model, context, options) => {
18264
+ const stream = agent.streamFunction;
18265
+ agent.streamFunction = (model, context, options) => {
18214
18266
  options?.signal?.throwIfAborted();
18215
18267
  state.rounds++;
18216
- const visible = new Set((context.tools ?? []).map((tool) => tool.name));
18268
+ const visible = new Set(getCurrentTools(context.messages).map((tool) => tool.name));
18217
18269
  const response = stream(model, context, options);
18218
18270
  return response instanceof Promise ? response.then((ready) => withCorrectedToolNames(state, ready, visible)) : withCorrectedToolNames(state, response, visible);
18219
18271
  };
18220
18272
  const before = agent.beforeToolCall;
18221
18273
  const after = agent.afterToolCall;
18274
+ const overLimit = /* @__PURE__ */ new Set();
18222
18275
  agent.beforeToolCall = async (event, signal) => {
18223
- if (signal?.aborted) return { block: true, reason: "Turn stopped." };
18276
+ if (signal?.aborted || overLimit.has(event.toolCall.id)) return { block: true, reason: "Turn stopped." };
18224
18277
  return before?.(event, signal);
18225
18278
  };
18279
+ const finish = agent.finishTurn;
18280
+ agent.finishTurn = (turn, signal) => state.reason === "tool-limit" ? { action: "end" } : finish?.(turn, signal);
18226
18281
  agent.afterToolCall = async (event, signal) => {
18227
18282
  const override = await after?.(event, signal);
18228
18283
  if (event.toolCall.name === LOAD && !event.isError) {
@@ -18233,31 +18288,39 @@ function configureAeroRuntime(agent, allowed, limits, progressive = true, pinned
18233
18288
  return override;
18234
18289
  };
18235
18290
  const toolStarts = /* @__PURE__ */ new Map();
18291
+ const toolDurations = /* @__PURE__ */ new Map();
18236
18292
  agent.subscribe((event) => {
18237
18293
  if (event.type === "tool_execution_start") {
18238
18294
  if (state.calls >= state.limits.maxToolCalls) {
18239
18295
  state.reason = "tool-limit";
18240
- agent.abort();
18296
+ overLimit.add(event.toolCallId);
18241
18297
  } else if (!agent.signal?.aborted) {
18242
18298
  state.calls++;
18243
- toolStarts.set(event.toolCallId, Date.now());
18244
18299
  }
18300
+ toolStarts.set(event.toolCallId, Date.now());
18301
+ }
18302
+ if (event.type === "tool_execution_end") {
18303
+ const startedAt = toolStarts.get(event.toolCallId);
18304
+ if (startedAt !== void 0) toolDurations.set(event.toolCallId, Date.now() - startedAt);
18305
+ toolStarts.delete(event.toolCallId);
18245
18306
  }
18246
18307
  if (event.type === "message_end" && event.message.role === "toolResult") {
18247
18308
  const message = event.message;
18248
18309
  explainMissingTool(state, message);
18249
- const startedAt = toolStarts.get(message.toolCallId);
18310
+ const durationMs = toolDurations.get(message.toolCallId);
18250
18311
  const requested = state.corrected.get(message.toolCallId);
18251
18312
  Object.assign(message, {
18252
18313
  aeroToolLabel: state.allowed.find((tool) => tool.name === message.toolName)?.label,
18253
- ...startedAt === void 0 ? {} : { aeroDurationMs: Date.now() - startedAt },
18314
+ ...durationMs === void 0 ? {} : { aeroDurationMs: durationMs },
18254
18315
  ...requested === void 0 ? {} : { aeroRequestedToolName: requested }
18255
18316
  });
18256
- toolStarts.delete(event.message.toolCallId);
18317
+ toolDurations.delete(message.toolCallId);
18257
18318
  state.corrected.delete(message.toolCallId);
18258
18319
  }
18259
18320
  if (event.type === "agent_start") {
18260
18321
  toolStarts.clear();
18322
+ toolDurations.clear();
18323
+ overLimit.clear();
18261
18324
  state.corrected.clear();
18262
18325
  state.calls = 0;
18263
18326
  state.rounds = 0;
@@ -18283,6 +18346,22 @@ function configureAeroRuntime(agent, allowed, limits, progressive = true, pinned
18283
18346
  runtime.progressive = progressive;
18284
18347
  runtime.pinned = new Set(pinned);
18285
18348
  agent.state.tools = visibleTools(runtime);
18349
+ setAeroSystemPrompt(agent);
18350
+ }
18351
+ function isSystemMessage(message) {
18352
+ return !!message && typeof message === "object" && message.role === "system";
18353
+ }
18354
+ function isRunFailureMessage(message) {
18355
+ if (!message || typeof message !== "object") return false;
18356
+ const m = message;
18357
+ if (m.role !== "assistant" || m.stopReason !== "aborted" && m.stopReason !== "error" || m.responseId) return false;
18358
+ const content = Array.isArray(m.content) ? m.content : [];
18359
+ return content.length === 1 && content[0].type === "text" && content[0].text === "" && m.usage?.totalTokens === 0;
18360
+ }
18361
+ function setAeroSystemPrompt(agent, prompt = agent.state.systemPrompt) {
18362
+ const system = createInitialSystemMessage(prompt, agent.state.tools.map(toToolDeclaration));
18363
+ const rest = agent.state.messages.filter((message) => message.role !== "system");
18364
+ agent.state.messages = system ? [system, ...rest] : rest;
18286
18365
  }
18287
18366
  function aeroTurnStatus(agent) {
18288
18367
  const runtime = runtimes.get(agent);
@@ -18296,7 +18375,7 @@ function aeroTurnStatus(agent) {
18296
18375
  }
18297
18376
 
18298
18377
  // src/agent/view-context.ts
18299
- import { Type as Type5 } from "@sinclair/typebox";
18378
+ import { Type as Type5 } from "@earendil-works/pi-ai";
18300
18379
  async function readAeroViewEvidence(options) {
18301
18380
  const { client, projectName, context } = options;
18302
18381
  if (context?.unavailableReason) throw validationError(context.unavailableReason);
@@ -18424,7 +18503,6 @@ function recordAgentToolEvent(args) {
18424
18503
  var builtinsRegistered = false;
18425
18504
  function ensureBuiltinsRegistered() {
18426
18505
  if (!builtinsRegistered) {
18427
- registerBuiltInApiProviders();
18428
18506
  validateAgentProviderRegistry();
18429
18507
  builtinsRegistered = true;
18430
18508
  }
@@ -18528,7 +18606,7 @@ function createAeroSession(opts) {
18528
18606
  tools,
18529
18607
  ...opts.initialMessages ? { messages: opts.initialMessages } : {}
18530
18608
  },
18531
- streamFn: opts.streamFn,
18609
+ streamFn: opts.streamFn ?? aeroStreamFn,
18532
18610
  sessionId: buildAeroProviderSessionId(opts),
18533
18611
  onPayload: splitAeroAnthropicSystemCachePayload,
18534
18612
  ...toolUsageHooks,
@@ -18540,6 +18618,7 @@ function createAeroSession(opts) {
18540
18618
  agent.subscribe((event) => {
18541
18619
  if (event.type !== "turn_end") return;
18542
18620
  if (event.message.role !== "assistant") return;
18621
+ if (isRunFailureMessage(event.message)) return;
18543
18622
  recordLlmUsageEvent({
18544
18623
  db: telemetryDb,
18545
18624
  projectId: opts.projectId,
@@ -18564,7 +18643,7 @@ function resolveSessionProviderAndModel(config, opts) {
18564
18643
  }
18565
18644
 
18566
18645
  // src/agent/remote-mcp.ts
18567
- import { Type as Type6 } from "@sinclair/typebox";
18646
+ import { Type as Type6 } from "@earendil-works/pi-ai";
18568
18647
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
18569
18648
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
18570
18649
  var log21 = createLogger("RemoteMcp");
@@ -18821,9 +18900,6 @@ Project shape: a Simple project with ${tracked} tracked queries and no measureme
18821
18900
  }
18822
18901
  }
18823
18902
 
18824
- // src/agent/compaction.ts
18825
- import { complete } from "@mariozechner/pi-ai";
18826
-
18827
18903
  // src/agent/compaction-config.ts
18828
18904
  var COMPACTION_TOKEN_THRESHOLD = 6e4;
18829
18905
  var COMPACTION_TARGET_RATIO = 0.5;
@@ -18875,8 +18951,12 @@ function estimateTranscriptTokens(messages) {
18875
18951
 
18876
18952
  // src/agent/compaction.ts
18877
18953
  function shouldCompact(messages) {
18878
- if (messages.length >= COMPACTION_MAX_MESSAGES) return true;
18879
- return estimateTranscriptTokens(messages) >= COMPACTION_TOKEN_THRESHOLD;
18954
+ const turns = conversationMessages(messages);
18955
+ if (turns.length >= COMPACTION_MAX_MESSAGES) return true;
18956
+ return estimateTranscriptTokens(turns) >= COMPACTION_TOKEN_THRESHOLD;
18957
+ }
18958
+ function conversationMessages(messages) {
18959
+ return messages.filter((message) => message.role !== "system");
18880
18960
  }
18881
18961
  function findSafeSplit(messages, targetIndex) {
18882
18962
  const maxSplit = messages.length - COMPACTION_PRESERVE_TAIL_MESSAGES;
@@ -18922,18 +19002,19 @@ async function runSummaryLlm(args) {
18922
19002
  messages: toLlmMessages(args.chunk)
18923
19003
  };
18924
19004
  const apiKey = args.getApiKey?.(args.model.provider);
18925
- const resp = await complete(args.model, context, apiKey ? { apiKey } : {});
19005
+ const resp = await completeOnce(args.model, context, apiKey ? { apiKey } : {});
18926
19006
  const parts = resp.content.filter((p) => p.type === "text");
18927
19007
  const text = parts.map((p) => p.text).join("\n").trim();
18928
19008
  if (!text) throw new Error("summary LLM returned no text content");
18929
19009
  return text;
18930
19010
  }
18931
19011
  async function compactMessages(args) {
18932
- const target = Math.floor(args.messages.length * COMPACTION_TARGET_RATIO);
18933
- const split = findSafeSplit(args.messages, target);
19012
+ const messages = conversationMessages(args.messages);
19013
+ const target = Math.floor(messages.length * COMPACTION_TARGET_RATIO);
19014
+ const split = findSafeSplit(messages, target);
18934
19015
  if (split === 0) return null;
18935
- const chunk = args.messages.slice(0, split);
18936
- const suffix = args.messages.slice(split);
19016
+ const chunk = messages.slice(0, split);
19017
+ const suffix = messages.slice(split);
18937
19018
  const summarize = args.summarize ?? runSummaryLlm;
18938
19019
  const rawSummary = await summarize({ model: args.model, chunk, getApiKey: args.getApiKey });
18939
19020
  const summary = truncateToByteLimit(rawSummary, AGENT_MEMORY_VALUE_MAX_BYTES);
@@ -18954,7 +19035,7 @@ function escapeMemoryFragment(value) {
18954
19035
  return value.replace(/<(\/?)memory>/gi, "<$1\u200Cmemory>");
18955
19036
  }
18956
19037
  function withoutPersistedToolDetails(messages) {
18957
- return messages.map((message) => {
19038
+ return messages.filter((message) => !isSystemMessage(message)).map((message) => {
18958
19039
  if (!message || typeof message !== "object") return message;
18959
19040
  const row = message;
18960
19041
  if (row.role !== "toolResult" || !("details" in row)) return message;
@@ -19182,7 +19263,8 @@ ${lines.join("\n")}
19182
19263
  if (!projectId) return;
19183
19264
  const row = this.loadRow(projectId);
19184
19265
  if (!row) return;
19185
- agent.state.systemPrompt = this.buildHydratedSystemPrompt(projectId, row.systemPrompt);
19266
+ if (agent.state.isStreaming) return;
19267
+ setAeroSystemPrompt(agent, this.buildHydratedSystemPrompt(projectId, row.systemPrompt));
19186
19268
  }
19187
19269
  /**
19188
19270
  * Acquire the Agent for an upcoming prompt/turn.
@@ -19214,10 +19296,7 @@ ${lines.join("\n")}
19214
19296
  const projectId = this.resolveProjectId(projectName);
19215
19297
  const row = this.loadRow(projectId);
19216
19298
  const systemPrompt = loadAeroSystemPrompt();
19217
- if (row && row.systemPrompt !== systemPrompt) {
19218
- this.persistPromptSnapshot(projectId, systemPrompt);
19219
- agent.state.systemPrompt = this.buildHydratedSystemPrompt(projectId, systemPrompt);
19220
- }
19299
+ if (row && row.systemPrompt !== systemPrompt) this.persistPromptSnapshot(projectId, systemPrompt);
19221
19300
  this.alignToolSurface(projectName, agent, {
19222
19301
  scope: preferences?.toolScope ?? AeroToolScopes.all,
19223
19302
  profile: preferences?.toolProfile ?? AeroToolProfiles.default
@@ -19232,7 +19311,7 @@ ${lines.join("\n")}
19232
19311
  preferences?.signal?.throwIfAborted();
19233
19312
  const progressive = (preferences?.toolProfile ?? AeroToolProfiles.default) === AeroToolProfiles.default;
19234
19313
  const shape = aeroProjectShape(this.opts.db, projectId);
19235
- agent.state.systemPrompt = this.buildHydratedSystemPrompt(projectId, systemPrompt) + shape.prompt + aeroViewPrompt(preferences?.context);
19314
+ setAeroSystemPrompt(agent, this.buildHydratedSystemPrompt(projectId, systemPrompt) + shape.prompt + aeroViewPrompt(preferences?.context));
19236
19315
  configureAeroRuntime(agent, [...agent.state.tools, ...progressive ? [buildAeroViewTool(view, evidence)] : []], preferences?.limits, progressive, shape.pinned);
19237
19316
  return agent;
19238
19317
  } finally {
@@ -19280,7 +19359,7 @@ ${lines.join("\n")}
19280
19359
  });
19281
19360
  if (!result2) return;
19282
19361
  agent.state.messages = result2.messages;
19283
- agent.state.systemPrompt = this.buildHydratedSystemPrompt(projectId, row.systemPrompt);
19362
+ setAeroSystemPrompt(agent, this.buildHydratedSystemPrompt(projectId, row.systemPrompt));
19284
19363
  this.save(projectName);
19285
19364
  log22.info("compaction.completed", {
19286
19365
  projectName,
@@ -19340,7 +19419,7 @@ ${lines.join("\n")}
19340
19419
  resolveTurnModel(preferences, current) {
19341
19420
  const pinned = resolveConfiguredAgentProvider(this.opts.config);
19342
19421
  const provider = preferences?.provider ?? pinned ?? current.provider ?? AgentProviderIds.claude;
19343
- const carried = !preferences?.provider && !pinned && provider === current.provider ? current.modelId ?? void 0 : void 0;
19422
+ const carried = !preferences?.provider && !pinned && provider === current.provider && current.modelId && isAgentModelAvailable(provider, current.modelId) ? current.modelId : void 0;
19344
19423
  const pinnedModelId = provider === pinned ? this.opts.config.agent?.model ?? void 0 : void 0;
19345
19424
  const modelId = preferences?.modelId ?? pinnedModelId ?? carried ?? getAgentProvider(provider).defaultModel;
19346
19425
  return { provider, modelId };
@@ -19692,12 +19771,12 @@ var ViewerAeroSessions = class {
19692
19771
  try {
19693
19772
  const client = new ApiClient(this.opts.selfApiUrl, minted.raw, { skipProbe: true, surface: "aero" });
19694
19773
  const agent = this.sessions.get(key)?.agent ?? this.createAgent(project, client);
19695
- agent.state.messages = trimViewerTranscript(agent.state.messages);
19774
+ agent.state.messages = trimViewerTranscript(agent.state.messages.filter((message) => message.role !== "system"));
19696
19775
  const view = { client, projectName: project.name, basePath: this.opts.config.basePath, context: preferences.context };
19697
19776
  const evidence = preferences.context ? await readAeroViewEvidence(view) : void 0;
19698
19777
  preferences.signal?.throwIfAborted();
19699
19778
  const shape = aeroProjectShape(this.opts.db, project.id);
19700
- agent.state.systemPrompt = loadAeroSystemPrompt(void 0, { extras: false }) + VIEWER_AERO_PROMPT + shape.prompt + aeroViewPrompt(preferences.context);
19779
+ setAeroSystemPrompt(agent, loadAeroSystemPrompt(void 0, { extras: false }) + VIEWER_AERO_PROMPT + shape.prompt + aeroViewPrompt(preferences.context));
19701
19780
  configureAeroRuntime(agent, [...buildViewerAeroTools(client, project.name, this.opts.managedSweeps), buildAeroViewTool(view, evidence)], void 0, true, shape.pinned);
19702
19781
  this.sessions.set(key, { agent, lastUsedAt: this.now(), updatedAt: this.sessions.get(key)?.updatedAt ?? null });
19703
19782
  this.turnsToday.set(key, { date: today, count: count3 + 1 });
@@ -19988,7 +20067,7 @@ function viewerAeroCaller(request, opts) {
19988
20067
  const principal = request.principal;
19989
20068
  return principal?.kind === "user" && principal.role === UserRoles.viewer ? principal.id : null;
19990
20069
  }
19991
- var VIEWER_HIDDEN_ASSISTANT_KEYS = ["api", "provider", "model", "responseId", "usage"];
20070
+ var VIEWER_HIDDEN_ASSISTANT_KEYS = ["api", "provider", "model", "responseId", "usage", "responseModel", "diagnostics", "providerThinkingLevel", "rawStopReason"];
19992
20071
  var VIEWER_ERROR_MESSAGE = "Aero could not finish this answer. Try again, or ask an administrator if it keeps happening.";
19993
20072
  function viewerRedact(_key, value) {
19994
20073
  if (!value || typeof value !== "object" || value.role !== "assistant") return value;
@@ -20043,6 +20122,11 @@ async function streamAgentTurn(reply, agent, turn) {
20043
20122
  };
20044
20123
  write({ type: "stream_open" });
20045
20124
  const unsubscribe = agent.subscribe((event) => {
20125
+ if ("message" in event && (isSystemMessage(event.message) || isRunFailureMessage(event.message))) return;
20126
+ if (event.type === "agent_end") {
20127
+ write({ ...event, messages: event.messages.filter((message) => !isSystemMessage(message)) });
20128
+ return;
20129
+ }
20046
20130
  if (event.type === "tool_execution_start") {
20047
20131
  const labelled = { ...event, label: agent.state.tools.find((tool) => tool.name === event.toolName)?.label };
20048
20132
  write(labelled);
@@ -20281,7 +20365,6 @@ function registerAgentRoutes(app, opts) {
20281
20365
  }
20282
20366
 
20283
20367
  // src/agent/recommendation-explainer.ts
20284
- import { complete as complete2 } from "@mariozechner/pi-ai";
20285
20368
  var RECOMMENDATION_EXPLAIN_PROMPT_VERSION = "v1";
20286
20369
  var SYSTEM_PROMPT = `You are an AEO (Answer Engine Optimization) analyst explaining why a specific content recommendation matters for a website. Your audience is the site owner or their agency \u2014 practical, time-poor, allergic to jargon.
20287
20370
 
@@ -20388,7 +20471,7 @@ function createRecommendationExplainer(opts) {
20388
20471
  ]
20389
20472
  };
20390
20473
  const apiKey = resolveApiKeyFor(provider, opts.config);
20391
- const resp = await complete2(model, context, apiKey ? { apiKey } : {});
20474
+ const resp = await completeOnce(model, context, apiKey ? { apiKey } : {});
20392
20475
  const parts = resp.content.filter((p) => p.type === "text");
20393
20476
  const text = parts.map((p) => p.text).join("\n").trim();
20394
20477
  if (!text) {
@@ -20466,7 +20549,7 @@ Your previous reply was not valid JSON. Return ONLY a JSON object with exactly t
20466
20549
  systemPrompt: BRIEF_SYSTEM_PROMPT,
20467
20550
  messages: [{ role: "user", content: userContent, timestamp: Date.now() }]
20468
20551
  };
20469
- const resp = await complete2(model, context, apiKey ? { apiKey } : {});
20552
+ const resp = await completeOnce(model, context, apiKey ? { apiKey } : {});
20470
20553
  totalCostDollars += Number.isFinite(resp.usage.cost.total) ? resp.usage.cost.total : 0;
20471
20554
  const parts = resp.content.filter((p) => p.type === "text");
20472
20555
  const text = parts.map((p) => p.text).join("\n").trim();
package/dist/cli.js CHANGED
@@ -35,7 +35,7 @@ import {
35
35
  trackCliCommandFinished,
36
36
  trackEvent,
37
37
  waitForServerRuntimeStartup
38
- } from "./chunk-7YRGVXR5.js";
38
+ } from "./chunk-6UO23AKO.js";
39
39
  import {
40
40
  autoSyncSkills,
41
41
  formatAutoSyncNotice
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  createGoogleMarketingCredentialStore,
4
4
  createGoogleMarketingRuntime,
5
5
  createServer
6
- } from "./chunk-7YRGVXR5.js";
6
+ } from "./chunk-6UO23AKO.js";
7
7
  import {
8
8
  loadConfig
9
9
  } from "./chunk-WZL73LY3.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonry/canonry",
3
- "version": "5.30.2",
3
+ "version": "5.32.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": [
@@ -53,7 +53,7 @@
53
53
  "LICENSE"
54
54
  ],
55
55
  "engines": {
56
- "node": ">=22.14.0 <27"
56
+ "node": ">=22.19.0 <27"
57
57
  },
58
58
  "dependencies": {
59
59
  "@anthropic-ai/sdk": "^0.91.1",
@@ -61,10 +61,9 @@
61
61
  "@fastify/rate-limit": "^11.2.0",
62
62
  "@fastify/static": "^9.1.1",
63
63
  "@google/genai": "^1.46.0",
64
- "@mariozechner/pi-agent-core": "0.67.6",
65
- "@mariozechner/pi-ai": "0.67.6",
64
+ "@earendil-works/pi-agent-core": "0.87.1",
65
+ "@earendil-works/pi-ai": "0.87.1",
66
66
  "@modelcontextprotocol/sdk": "^1.29.0",
67
- "@sinclair/typebox": "^0.34.41",
68
67
  "better-sqlite3": "^12.11.1",
69
68
  "chrome-remote-interface": "^0.33.2",
70
69
  "cron-parser": "^5.5.0",
@@ -88,32 +87,32 @@
88
87
  "tsx": "^4.19.0",
89
88
  "@ainyc/canonry-web": "0.0.0",
90
89
  "@ainyc/canonry-api-client": "0.0.0",
91
- "@ainyc/canonry-config": "0.0.0",
92
- "@ainyc/canonry-contracts": "0.0.0",
93
90
  "@ainyc/canonry-api-routes": "0.0.0",
91
+ "@ainyc/canonry-config": "0.0.0",
94
92
  "@ainyc/canonry-db": "0.0.0",
93
+ "@ainyc/canonry-contracts": "0.0.0",
95
94
  "@ainyc/canonry-integration-bing": "0.0.0",
96
95
  "@ainyc/canonry-integration-cloud-run": "0.0.0",
97
96
  "@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
98
- "@ainyc/canonry-integration-commoncrawl": "0.0.0",
99
- "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
100
97
  "@ainyc/canonry-integration-google": "0.0.0",
101
98
  "@ainyc/canonry-integration-google-ads": "0.0.0",
99
+ "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
100
+ "@ainyc/canonry-integration-commoncrawl": "0.0.0",
102
101
  "@ainyc/canonry-integration-google-business-profile": "0.0.0",
103
102
  "@ainyc/canonry-integration-google-places": "0.0.0",
104
103
  "@ainyc/canonry-integration-google-tag-manager": "0.0.0",
105
104
  "@ainyc/canonry-integration-openai-ads": "0.0.0",
106
105
  "@ainyc/canonry-integration-traffic": "0.0.0",
107
106
  "@ainyc/canonry-integration-wordpress": "0.0.0",
108
- "@ainyc/canonry-intelligence": "0.0.0",
109
- "@ainyc/canonry-provider-gemini": "0.0.0",
110
107
  "@ainyc/canonry-provider-claude": "0.0.0",
108
+ "@ainyc/canonry-provider-cdp": "0.0.0",
109
+ "@ainyc/canonry-provider-gemini": "0.0.0",
111
110
  "@ainyc/canonry-provider-local": "0.0.0",
111
+ "@ainyc/canonry-intelligence": "0.0.0",
112
112
  "@ainyc/canonry-provider-muse": "0.0.0",
113
113
  "@ainyc/canonry-provider-openai": "0.0.0",
114
- "@ainyc/canonry-integration-typesafe": "0.0.0",
115
114
  "@ainyc/canonry-provider-perplexity": "0.0.0",
116
- "@ainyc/canonry-provider-cdp": "0.0.0"
115
+ "@ainyc/canonry-integration-typesafe": "0.0.0"
117
116
  },
118
117
  "scripts": {
119
118
  "build": "pnpm run build:cli && pnpm run build:web",