@ainyc/canonry 4.98.0 → 4.99.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.
@@ -9,7 +9,7 @@ import {
9
9
  loadConfig,
10
10
  loadConfigRaw,
11
11
  saveConfigPatch
12
- } from "./chunk-3TH54726.js";
12
+ } from "./chunk-VQN74WWA.js";
13
13
  import {
14
14
  CC_CACHE_DIR,
15
15
  DUCKDB_SPEC,
@@ -8331,13 +8331,63 @@ function buildSkillDocTools() {
8331
8331
  // src/agent/mcp-to-agent-tool.ts
8332
8332
  import { Type as Type2 } from "@sinclair/typebox";
8333
8333
  var MAX_TOOL_RESULT_CHARS = 2e4;
8334
- function truncate(json) {
8335
- if (json.length <= MAX_TOOL_RESULT_CHARS) return json;
8336
- return json.slice(0, MAX_TOOL_RESULT_CHARS) + "\n... (truncated \u2014 result too large)";
8334
+ var TRUNCATION_NOTE = "... (truncated \u2014 result too large)";
8335
+ function serializeResult(value) {
8336
+ return JSON.stringify(value, null, 2);
8337
+ }
8338
+ function largestArrayKey(obj) {
8339
+ let best;
8340
+ let bestLen = -1;
8341
+ for (const [k, v] of Object.entries(obj)) {
8342
+ if (!Array.isArray(v)) continue;
8343
+ const len = serializeResult(v).length;
8344
+ if (len > bestLen) {
8345
+ bestLen = len;
8346
+ best = k;
8347
+ }
8348
+ }
8349
+ return best;
8350
+ }
8351
+ function trimRowsToFit(rows, render) {
8352
+ let kept = rows.slice();
8353
+ while (kept.length > 0 && render(kept).length > MAX_TOOL_RESULT_CHARS) {
8354
+ kept = kept.slice(0, -1);
8355
+ }
8356
+ return kept;
8357
+ }
8358
+ function truncateToolResult(details) {
8359
+ const full = serializeResult(details);
8360
+ if (full.length <= MAX_TOOL_RESULT_CHARS) return full;
8361
+ if (Array.isArray(details)) {
8362
+ const kept = trimRowsToFit(
8363
+ details,
8364
+ (rows) => serializeResult({ items: rows, __truncated: true, __omittedRows: details.length - rows.length })
8365
+ );
8366
+ return serializeResult({ items: kept, __truncated: true, __omittedRows: details.length - kept.length });
8367
+ }
8368
+ if (details && typeof details === "object") {
8369
+ const obj = details;
8370
+ const arrayKey = largestArrayKey(obj);
8371
+ if (arrayKey) {
8372
+ const rows = obj[arrayKey];
8373
+ const kept = trimRowsToFit(
8374
+ rows,
8375
+ (r) => serializeResult({ ...obj, [arrayKey]: r, __truncated: true, __omittedRows: rows.length - r.length })
8376
+ );
8377
+ const out = serializeResult({
8378
+ ...obj,
8379
+ [arrayKey]: kept,
8380
+ __truncated: true,
8381
+ __omittedRows: rows.length - kept.length
8382
+ });
8383
+ if (out.length <= MAX_TOOL_RESULT_CHARS) return out;
8384
+ }
8385
+ }
8386
+ return full.slice(0, MAX_TOOL_RESULT_CHARS) + "\n" + TRUNCATION_NOTE;
8337
8387
  }
8338
8388
  function textResult2(details) {
8339
8389
  return {
8340
- content: [{ type: "text", text: truncate(JSON.stringify(details, null, 2)) }],
8390
+ content: [{ type: "text", text: truncateToolResult(details) }],
8341
8391
  details
8342
8392
  };
8343
8393
  }
@@ -8404,13 +8454,31 @@ function loadAeroSystemPrompt(pkgDir) {
8404
8454
  const skillDir = resolveAeroSkillDir(pkgDir);
8405
8455
  const skillBody = fs7.readFileSync(path8.join(skillDir, "SKILL.md"), "utf-8");
8406
8456
  const soulPath = path8.join(skillDir, "soul.md");
8407
- if (!fs7.existsSync(soulPath)) return skillBody;
8408
- const soulBody = fs7.readFileSync(soulPath, "utf-8");
8409
- return `${soulBody.trimEnd()}
8457
+ const base = fs7.existsSync(soulPath) ? `${fs7.readFileSync(soulPath, "utf-8").trimEnd()}
8410
8458
 
8411
8459
  ---
8412
8460
 
8413
- ${skillBody}`;
8461
+ ${skillBody}` : skillBody;
8462
+ return appendSystemPromptExtras(base);
8463
+ }
8464
+ function appendSystemPromptExtras(base, env = process.env) {
8465
+ const inline = env.AERO_SYSTEM_PROMPT_APPEND?.trim();
8466
+ let fileBody = "";
8467
+ const filePath = env.AERO_SYSTEM_PROMPT_FILE?.trim();
8468
+ if (filePath) {
8469
+ try {
8470
+ fileBody = fs7.readFileSync(filePath, "utf-8").trim();
8471
+ } catch {
8472
+ fileBody = "";
8473
+ }
8474
+ }
8475
+ const extras = [inline, fileBody].filter((s) => !!s && s.length > 0);
8476
+ if (extras.length === 0) return base;
8477
+ return `${base.trimEnd()}
8478
+
8479
+ ---
8480
+
8481
+ ${extras.join("\n\n")}`;
8414
8482
  }
8415
8483
  function missingProviderMessage() {
8416
8484
  const configHints = agentProvidersByPriority().join(", ");
@@ -8461,6 +8529,93 @@ function resolveSessionProviderAndModel(config, opts) {
8461
8529
  return { provider, modelId };
8462
8530
  }
8463
8531
 
8532
+ // src/agent/remote-mcp.ts
8533
+ import { Type as Type3 } from "@sinclair/typebox";
8534
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
8535
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
8536
+ var log18 = createLogger("RemoteMcp");
8537
+ var AERO_EXCLUDED_MCP_TOOLS2 = /* @__PURE__ */ new Set([]);
8538
+ var MAX_TOOL_RESULT_CHARS2 = 2e4;
8539
+ function truncate(text) {
8540
+ if (text.length <= MAX_TOOL_RESULT_CHARS2) return text;
8541
+ return text.slice(0, MAX_TOOL_RESULT_CHARS2) + "\n... (truncated, result too large)";
8542
+ }
8543
+ async function connectStreamableHttp(server) {
8544
+ const transport = new StreamableHTTPClientTransport(new URL(server.url), {
8545
+ requestInit: {
8546
+ headers: { Authorization: `Bearer ${server.token}` }
8547
+ }
8548
+ });
8549
+ const client = new Client(
8550
+ { name: "canonry-aero", version: "1.0.0" },
8551
+ { capabilities: {} }
8552
+ );
8553
+ await client.connect(transport);
8554
+ return client;
8555
+ }
8556
+ function isReadOnly(tool) {
8557
+ return tool.annotations?.readOnlyHint === true;
8558
+ }
8559
+ function adaptRemoteTool(client, tool) {
8560
+ const parameters = Type3.Unsafe(
8561
+ tool.inputSchema ?? { type: "object", properties: {} }
8562
+ );
8563
+ const execute = async (_toolCallId, params) => {
8564
+ const result = await client.callTool({ name: tool.name, arguments: params });
8565
+ return {
8566
+ content: [{ type: "text", text: truncate(JSON.stringify(result, null, 2)) }],
8567
+ details: result
8568
+ };
8569
+ };
8570
+ return {
8571
+ name: tool.name,
8572
+ label: tool.annotations?.title ?? tool.name,
8573
+ description: tool.description ?? "",
8574
+ parameters,
8575
+ execute
8576
+ };
8577
+ }
8578
+ async function loadExternalMcpTools(servers, opts = {}) {
8579
+ if (!servers || servers.length === 0) return [];
8580
+ const connect = opts.connect ?? connectStreamableHttp;
8581
+ const excluded = opts.excluded ?? AERO_EXCLUDED_MCP_TOOLS2;
8582
+ const tools = [];
8583
+ for (const server of servers) {
8584
+ const label = server.label ?? server.url;
8585
+ let client;
8586
+ try {
8587
+ client = await connect(server);
8588
+ } catch (err) {
8589
+ log18.error("external-mcp.connect-failed", {
8590
+ label,
8591
+ error: err instanceof Error ? err.message : String(err)
8592
+ });
8593
+ continue;
8594
+ }
8595
+ let listed;
8596
+ try {
8597
+ listed = await client.listTools();
8598
+ } catch (err) {
8599
+ log18.error("external-mcp.list-failed", {
8600
+ label,
8601
+ error: err instanceof Error ? err.message : String(err)
8602
+ });
8603
+ continue;
8604
+ }
8605
+ for (const tool of listed.tools) {
8606
+ if (excluded.has(tool.name)) continue;
8607
+ if (!isReadOnly(tool)) continue;
8608
+ tools.push(adaptRemoteTool(client, tool));
8609
+ }
8610
+ log18.info("external-mcp.loaded", {
8611
+ label,
8612
+ discovered: listed.tools.length,
8613
+ adopted: tools.length
8614
+ });
8615
+ }
8616
+ return tools;
8617
+ }
8618
+
8464
8619
  // src/agent/memory-store.ts
8465
8620
  import crypto18 from "crypto";
8466
8621
  import { and as and13, desc as desc6, eq as eq17, like, sql as sql5 } from "drizzle-orm";
@@ -8684,7 +8839,7 @@ async function compactMessages(args) {
8684
8839
  }
8685
8840
 
8686
8841
  // src/agent/session-registry.ts
8687
- var log18 = createLogger("SessionRegistry");
8842
+ var log19 = createLogger("SessionRegistry");
8688
8843
  var MAX_HYDRATE_NOTES = 20;
8689
8844
  var MAX_HYDRATE_BYTES = 32 * 1024;
8690
8845
  function escapeMemoryFragment(value) {
@@ -8703,10 +8858,53 @@ var SessionRegistry = class {
8703
8858
  * awaits the same promise instead of kicking off a duplicate LLM call.
8704
8859
  */
8705
8860
  compactions = /* @__PURE__ */ new Map();
8861
+ /**
8862
+ * Read-only tools loaded once from the injected remote MCP servers (OSS-A).
8863
+ * The server set is static config, so we connect + adapt a single time and
8864
+ * cache the promise; every session merges the same AgentTool list. Resolves
8865
+ * to `[]` when no servers are configured or all fail to connect (the load is
8866
+ * fail-soft and never throws).
8867
+ */
8868
+ externalToolsPromise;
8706
8869
  opts;
8707
8870
  constructor(opts) {
8708
8871
  this.opts = opts;
8709
8872
  }
8873
+ /**
8874
+ * Lazily load + cache the injected remote MCP tools. The seam OSS-A exposes:
8875
+ * `loadExternalMcpTools` connects to each configured server over the frozen
8876
+ * bearer-gated Streamable HTTP transport, lists tools, and adapts the
8877
+ * read-only, non-excluded ones into AgentTools. Cached so we connect once per
8878
+ * registry lifetime regardless of how many projects or turns run.
8879
+ */
8880
+ loadExternalTools() {
8881
+ if (!this.externalToolsPromise) {
8882
+ this.externalToolsPromise = loadExternalMcpTools(this.opts.config.externalMcpServers).catch(
8883
+ (err) => {
8884
+ log19.error("external-mcp.load-failed", {
8885
+ error: err instanceof Error ? err.message : String(err)
8886
+ });
8887
+ return [];
8888
+ }
8889
+ );
8890
+ }
8891
+ return this.externalToolsPromise;
8892
+ }
8893
+ /**
8894
+ * Append the cached external MCP tools to the live agent's tool list, idempotently.
8895
+ * Called after scope/model alignment in `acquireForTurn` (which rebuilds
8896
+ * `state.tools` from the local registry), so the remote read-only tools ride
8897
+ * alongside the local ones for the upcoming turn. No-op when none are configured.
8898
+ */
8899
+ async mergeExternalTools(agent) {
8900
+ const external = await this.loadExternalTools();
8901
+ if (external.length === 0) return;
8902
+ const current = agent.state.tools;
8903
+ const present = new Set(current.map((t) => t.name));
8904
+ const additions = external.filter((t) => !present.has(t.name));
8905
+ if (additions.length === 0) return;
8906
+ agent.state.tools = [...current, ...additions];
8907
+ }
8710
8908
  /** Read-only access to the config snapshot the registry was built with. */
8711
8909
  getConfig() {
8712
8910
  return this.opts.config;
@@ -8865,6 +9063,7 @@ ${lines.join("\n")}
8865
9063
  if (preferences?.provider || preferences?.modelId) {
8866
9064
  this.alignModel(projectName, agent, preferences);
8867
9065
  }
9066
+ await this.mergeExternalTools(agent);
8868
9067
  await this.maybeCompact(projectName, agent);
8869
9068
  return agent;
8870
9069
  }
@@ -8911,13 +9110,13 @@ ${lines.join("\n")}
8911
9110
  agent.state.messages = result.messages;
8912
9111
  agent.state.systemPrompt = this.buildHydratedSystemPrompt(projectId, row.systemPrompt);
8913
9112
  this.save(projectName);
8914
- log18.info("compaction.completed", {
9113
+ log19.info("compaction.completed", {
8915
9114
  projectName,
8916
9115
  removedCount: result.removedCount,
8917
9116
  summaryBytes: Buffer.byteLength(result.summary, "utf8")
8918
9117
  });
8919
9118
  } catch (err) {
8920
- log18.error("compaction.failed", {
9119
+ log19.error("compaction.failed", {
8921
9120
  projectName,
8922
9121
  error: err instanceof Error ? err.message : String(err)
8923
9122
  });
@@ -9014,7 +9213,7 @@ ${lines.join("\n")}
9014
9213
  await agent.prompt(msgs);
9015
9214
  this.save(projectName);
9016
9215
  } catch (err) {
9017
- log18.error("drain.failed", {
9216
+ log19.error("drain.failed", {
9018
9217
  projectName,
9019
9218
  error: err instanceof Error ? err.message : String(err)
9020
9219
  });
@@ -9612,7 +9811,7 @@ function formatAuditFactorScore(factor) {
9612
9811
  }
9613
9812
 
9614
9813
  // src/snapshot-service.ts
9615
- var log19 = createLogger("Snapshot");
9814
+ var log20 = createLogger("Snapshot");
9616
9815
  var ANALYSIS_PROVIDER_PRIORITY = ["openai", "claude", "gemini", "perplexity", "local"];
9617
9816
  var SNAPSHOT_QUERY_COUNT = 6;
9618
9817
  var ProviderExecutionGate2 = class {
@@ -9758,7 +9957,7 @@ var SnapshotService = class {
9758
9957
  return mapAuditReport(report);
9759
9958
  } catch (err) {
9760
9959
  const message = err instanceof Error ? err.message : String(err);
9761
- log19.warn("audit.failed", { homepageUrl, error: message });
9960
+ log20.warn("audit.failed", { homepageUrl, error: message });
9762
9961
  return {
9763
9962
  url: homepageUrl,
9764
9963
  finalUrl: homepageUrl,
@@ -9787,7 +9986,7 @@ var SnapshotService = class {
9787
9986
  queries: parsedQueries
9788
9987
  };
9789
9988
  } catch (err) {
9790
- log19.warn("profile.generation-failed", {
9989
+ log20.warn("profile.generation-failed", {
9791
9990
  domain: ctx.domain,
9792
9991
  provider: ctx.analysisProvider.adapter.name,
9793
9992
  error: err instanceof Error ? err.message : String(err)
@@ -9929,7 +10128,7 @@ var SnapshotService = class {
9929
10128
  recommendedActions: uniqueStrings(parsed.recommendedActions ?? []).slice(0, 4)
9930
10129
  };
9931
10130
  } catch (err) {
9932
- log19.warn("response.analysis-failed", {
10131
+ log20.warn("response.analysis-failed", {
9933
10132
  provider: ctx.analysisProvider.adapter.name,
9934
10133
  error: err instanceof Error ? err.message : String(err)
9935
10134
  });
@@ -10211,7 +10410,7 @@ function clipText(value, length) {
10211
10410
  // src/server.ts
10212
10411
  var _require3 = createRequire3(import.meta.url);
10213
10412
  var { version: PKG_VERSION2 } = _require3("../package.json");
10214
- var log20 = createLogger("Server");
10413
+ var log21 = createLogger("Server");
10215
10414
  var DEFAULT_QUOTA = {
10216
10415
  maxConcurrency: 2,
10217
10416
  maxRequestsPerMinute: 10,
@@ -10344,7 +10543,7 @@ function applyLegacyCredentials(rows, config) {
10344
10543
  }
10345
10544
  if (migratedGoogle > 0) {
10346
10545
  saveConfigPatch({ google: config.google });
10347
- log20.info("credentials.migrated", { type: "google", count: migratedGoogle });
10546
+ log21.info("credentials.migrated", { type: "google", count: migratedGoogle });
10348
10547
  }
10349
10548
  let migratedGa4 = 0;
10350
10549
  for (const row of rows.ga4) {
@@ -10362,7 +10561,7 @@ function applyLegacyCredentials(rows, config) {
10362
10561
  }
10363
10562
  if (migratedGa4 > 0) {
10364
10563
  saveConfigPatch({ ga4: config.ga4 });
10365
- log20.info("credentials.migrated", { type: "ga4", count: migratedGa4 });
10564
+ log21.info("credentials.migrated", { type: "ga4", count: migratedGa4 });
10366
10565
  }
10367
10566
  }
10368
10567
  function isLoopbackBindHost(host) {
@@ -10401,11 +10600,11 @@ async function createServer(opts) {
10401
10600
  applyLegacyCredentials(legacyRows, opts.config);
10402
10601
  dropLegacyCredentialColumns(opts.db);
10403
10602
  } catch (err) {
10404
- log20.warn("credentials.migration.failed", {
10603
+ log21.warn("credentials.migration.failed", {
10405
10604
  error: err instanceof Error ? err.message : String(err)
10406
10605
  });
10407
10606
  }
10408
- log20.info("providers.configured", { providers: Object.keys(providers).filter((k) => {
10607
+ log21.info("providers.configured", { providers: Object.keys(providers).filter((k) => {
10409
10608
  const p = providers[k];
10410
10609
  return p?.apiKey || p?.baseUrl || p?.vertexProject;
10411
10610
  }) });
@@ -118,8 +118,34 @@ Do not write config.yaml by hand; use "canonry init", "canonry settings", or "ca
118
118
  } catch {
119
119
  }
120
120
  }
121
+ const parsedExternalMcp = parseExternalMcpEnv(process.env.CANONRY_EXTERNAL_MCP);
122
+ if (parsedExternalMcp) {
123
+ parsed.externalMcpServers = parsedExternalMcp;
124
+ }
121
125
  return parsed;
122
126
  }
127
+ function parseExternalMcpEnv(raw) {
128
+ const trimmed = raw?.trim();
129
+ if (!trimmed) return void 0;
130
+ let value;
131
+ try {
132
+ value = JSON.parse(trimmed);
133
+ } catch {
134
+ return void 0;
135
+ }
136
+ if (!Array.isArray(value)) return void 0;
137
+ const servers = [];
138
+ for (const entry of value) {
139
+ if (!entry || typeof entry !== "object") continue;
140
+ const candidate = entry;
141
+ const url = typeof candidate.url === "string" ? candidate.url.trim() : "";
142
+ const token = typeof candidate.token === "string" ? candidate.token : "";
143
+ if (!url || !token) continue;
144
+ const label = typeof candidate.label === "string" ? candidate.label : void 0;
145
+ servers.push({ url, token, ...label ? { label } : {} });
146
+ }
147
+ return servers.length > 0 ? servers : void 0;
148
+ }
123
149
  function loadConfigRaw() {
124
150
  const configPath = getConfigPath();
125
151
  if (!fs.existsSync(configPath)) return null;
package/dist/cli.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  setTelemetrySource,
28
28
  showFirstRunNotice,
29
29
  trackEvent
30
- } from "./chunk-7COFPNMH.js";
30
+ } from "./chunk-CEHD33NQ.js";
31
31
  import {
32
32
  CliError,
33
33
  EXIT_SYSTEM_ERROR,
@@ -44,7 +44,7 @@ import {
44
44
  saveConfig,
45
45
  saveConfigPatch,
46
46
  usageError
47
- } from "./chunk-3TH54726.js";
47
+ } from "./chunk-VQN74WWA.js";
48
48
  import {
49
49
  apiKeys,
50
50
  createClient,
package/dist/index.d.ts CHANGED
@@ -156,6 +156,21 @@ interface VercelTrafficConnectionConfigEntry {
156
156
  interface VercelTrafficConfigEntry {
157
157
  connections?: VercelTrafficConnectionConfigEntry[];
158
158
  }
159
+ /**
160
+ * One injected remote MCP server Aero loads read-only tools from (OSS-A).
161
+ * Generic shape: `{ url, token, label? }`. The transport is bearer-gated MCP
162
+ * Streamable HTTP (the contract is frozen in `agent/remote-mcp.ts`). The
163
+ * server is remote, never co-located in the OSS container; per-tenant
164
+ * isolation is the token's responsibility, not the container boundary.
165
+ */
166
+ interface ExternalMcpServerConfig {
167
+ /** Streamable HTTP endpoint of the remote MCP server. */
168
+ url: string;
169
+ /** Bearer token sent as `Authorization: Bearer <token>`. */
170
+ token: string;
171
+ /** Optional human label used in logs. Defaults to `url`. */
172
+ label?: string;
173
+ }
159
174
  interface AgentConfigEntry {
160
175
  /**
161
176
  * Agent mode. `'disabled'` turns the built-in Aero agent OFF entirely — the
@@ -212,6 +227,7 @@ interface CanonryConfig {
212
227
  lastUpdateCheckAt?: string;
213
228
  lastKnownLatestVersion?: string;
214
229
  agent?: AgentConfigEntry;
230
+ externalMcpServers?: ExternalMcpServerConfig[];
215
231
  places?: PlacesConfigEntry;
216
232
  embed?: EmbedConfigEntry;
217
233
  }
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  createServer
3
- } from "./chunk-7COFPNMH.js";
3
+ } from "./chunk-CEHD33NQ.js";
4
4
  import {
5
5
  loadConfig
6
- } from "./chunk-3TH54726.js";
6
+ } from "./chunk-VQN74WWA.js";
7
7
  import "./chunk-DJVUNURL.js";
8
8
  import "./chunk-CWFTMFEU.js";
9
9
  export {
package/dist/mcp.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  PACKAGE_VERSION,
4
4
  canonryMcpTools,
5
5
  createApiClient
6
- } from "./chunk-3TH54726.js";
6
+ } from "./chunk-VQN74WWA.js";
7
7
  import {
8
8
  isReadOnlyKey
9
9
  } from "./chunk-CWFTMFEU.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainyc/canonry",
3
- "version": "4.98.0",
3
+ "version": "4.99.0",
4
4
  "type": "module",
5
5
  "description": "Agent-first open-source AEO operating platform - track how answer engines cite your domain",
6
6
  "license": "FSL-1.1-ALv2",
@@ -62,27 +62,27 @@
62
62
  "@types/node-cron": "^3.0.11",
63
63
  "tsup": "^8.5.1",
64
64
  "tsx": "^4.19.0",
65
- "@ainyc/canonry-api-routes": "0.0.0",
66
65
  "@ainyc/canonry-api-client": "0.0.0",
67
- "@ainyc/canonry-config": "0.0.0",
66
+ "@ainyc/canonry-api-routes": "0.0.0",
68
67
  "@ainyc/canonry-contracts": "0.0.0",
69
- "@ainyc/canonry-integration-bing": "0.0.0",
68
+ "@ainyc/canonry-config": "0.0.0",
70
69
  "@ainyc/canonry-db": "0.0.0",
71
70
  "@ainyc/canonry-integration-openai-ads": "0.0.0",
71
+ "@ainyc/canonry-integration-bing": "0.0.0",
72
72
  "@ainyc/canonry-integration-cloud-run": "0.0.0",
73
73
  "@ainyc/canonry-integration-commoncrawl": "0.0.0",
74
74
  "@ainyc/canonry-integration-google": "0.0.0",
75
75
  "@ainyc/canonry-integration-google-business-profile": "0.0.0",
76
- "@ainyc/canonry-integration-wordpress": "0.0.0",
76
+ "@ainyc/canonry-integration-traffic": "0.0.0",
77
77
  "@ainyc/canonry-intelligence": "0.0.0",
78
78
  "@ainyc/canonry-integration-google-places": "0.0.0",
79
- "@ainyc/canonry-provider-claude": "0.0.0",
80
- "@ainyc/canonry-integration-traffic": "0.0.0",
81
79
  "@ainyc/canonry-provider-cdp": "0.0.0",
80
+ "@ainyc/canonry-provider-claude": "0.0.0",
82
81
  "@ainyc/canonry-provider-gemini": "0.0.0",
82
+ "@ainyc/canonry-integration-wordpress": "0.0.0",
83
83
  "@ainyc/canonry-provider-local": "0.0.0",
84
- "@ainyc/canonry-provider-perplexity": "0.0.0",
85
- "@ainyc/canonry-provider-openai": "0.0.0"
84
+ "@ainyc/canonry-provider-openai": "0.0.0",
85
+ "@ainyc/canonry-provider-perplexity": "0.0.0"
86
86
  },
87
87
  "scripts": {
88
88
  "build": "tsx scripts/copy-agent-assets.ts && tsup && tsx build-web.ts",