@ainyc/canonry 5.6.0 → 5.8.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 (35) hide show
  1. package/assets/agent-workspace/skills/aero/SKILL.md +18 -0
  2. package/assets/agent-workspace/skills/aero/soul.md +2 -0
  3. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +5 -4
  4. package/assets/assets/{AuditHistoryPanel-DVUbjx1M.js → AuditHistoryPanel-DumM4n44.js} +1 -1
  5. package/assets/assets/{BacklinksPage-D01VTDAp.js → BacklinksPage-DHW-aDYf.js} +1 -1
  6. package/assets/assets/{HistoryPage-Bk1gsXe5.js → HistoryPage-DJgCpcmY.js} +1 -1
  7. package/assets/assets/{MeasurementPropertyPage-D7p5vpY_.js → MeasurementPropertyPage-CblC7DUV.js} +1 -1
  8. package/assets/assets/{ProjectPage-Dv3USEIE.js → ProjectPage-BKZyG1S3.js} +1 -1
  9. package/assets/assets/{RunRow-Dpa0kAip.js → RunRow-plH2LI7o.js} +1 -1
  10. package/assets/assets/{RunsPage-B59z7DQ_.js → RunsPage-JCSy7P9I.js} +1 -1
  11. package/assets/assets/{SettingsPage-HJDgmQi9.js → SettingsPage-B8KXT4YK.js} +1 -1
  12. package/assets/assets/{SiteHealthSection-BnJA-zvQ.js → SiteHealthSection-06pNjngi.js} +3 -3
  13. package/assets/assets/{TrafficPage-C8na59q_.js → TrafficPage-BYLDNQ1A.js} +1 -1
  14. package/assets/assets/{TrafficSourceDetailPage-B2fO8bDc.js → TrafficSourceDetailPage-CxseBpYr.js} +1 -1
  15. package/assets/assets/{extract-error-message-670MySRM.js → extract-error-message-Dv5fwiqy.js} +1 -1
  16. package/assets/assets/index-2Ldc0Hq4.js +86 -0
  17. package/assets/assets/{index-PT4UiPTM.css → index-neNKCg78.css} +1 -1
  18. package/assets/assets/{react-sigma_core.esm.min-DOhuYLQj.js → react-sigma_core.esm.min-C8381RhL.js} +1 -1
  19. package/assets/assets/{v2-overview-adapter-CQyDNkXa.js → v2-overview-adapter-BQD1Ap8K.js} +1 -1
  20. package/assets/assets/vendor-markdown-B4giJcws.js +29 -0
  21. package/assets/index.html +3 -3
  22. package/dist/{chunk-TM2NQMJA.js → chunk-5E4ASNU4.js} +54 -7
  23. package/dist/{chunk-C4KBR4H6.js → chunk-BO53QARL.js} +48 -15
  24. package/dist/{chunk-GHAUT7XJ.js → chunk-LR7BVM53.js} +1 -1
  25. package/dist/{chunk-KZYOJ4F5.js → chunk-R4COV5KR.js} +1 -1
  26. package/dist/{chunk-I4AHG7K3.js → chunk-S4DBS5DP.js} +1 -1
  27. package/dist/cli.js +6 -6
  28. package/dist/{demo-server-JEGVGOPQ.js → demo-server-WRVVNKLS.js} +2 -2
  29. package/dist/index.d.ts +20 -8
  30. package/dist/index.js +4 -4
  31. package/dist/{intelligence-service-5P62KKG6.js → intelligence-service-4XMMO3JK.js} +1 -1
  32. package/dist/mcp.js +2 -2
  33. package/package.json +11 -11
  34. package/assets/assets/index-DTrkuKOh.js +0 -86
  35. package/assets/assets/vendor-markdown-B-Oq2NYq.js +0 -14
package/assets/index.html CHANGED
@@ -12,14 +12,14 @@
12
12
  <link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
13
13
  <link rel="apple-touch-icon" href="./apple-touch-icon.png" />
14
14
  <title>Canonry</title>
15
- <script type="module" crossorigin src="./assets/index-DTrkuKOh.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-2Ldc0Hq4.js"></script>
16
16
  <link rel="modulepreload" crossorigin href="./assets/vendor-tanstack-ZXXkndVj.js">
17
17
  <link rel="modulepreload" crossorigin href="./assets/vendor-radix-twS1M3FF.js">
18
18
  <link rel="modulepreload" crossorigin href="./assets/vendor-recharts-llkUFBGR.js">
19
19
  <link rel="modulepreload" crossorigin href="./assets/vendor-lucide-CneDbhOr.js">
20
- <link rel="modulepreload" crossorigin href="./assets/vendor-markdown-B-Oq2NYq.js">
20
+ <link rel="modulepreload" crossorigin href="./assets/vendor-markdown-B4giJcws.js">
21
21
  <link rel="modulepreload" crossorigin href="./assets/vendor-yaml-GWJz5H2G.js">
22
- <link rel="stylesheet" crossorigin href="./assets/index-PT4UiPTM.css">
22
+ <link rel="stylesheet" crossorigin href="./assets/index-neNKCg78.css">
23
23
  </head>
24
24
  <body>
25
25
  <div id="root"></div>
@@ -7406,6 +7406,26 @@ var MIGRATION_VERSIONS = [
7406
7406
  statements: [
7407
7407
  `ALTER TABLE doctor_health_state ADD COLUMN failing_signature TEXT`
7408
7408
  ]
7409
+ },
7410
+ {
7411
+ version: 158,
7412
+ name: "deepinfra-agent-default-model",
7413
+ // Move the previous agent default once. A later explicit GLM selection
7414
+ // must survive subsequent boots and session hydration.
7415
+ //
7416
+ // A run hook rather than a statement for two reasons. The statement
7417
+ // allowlist is schema-shaped and this rewrites VALUES, and a supported
7418
+ // legacy database can reach this version with no `agent_sessions` at
7419
+ // all: one stamped in `_migrations` at a version above 38 resumes past
7420
+ // the migration that introduces the table, so an unguarded UPDATE dies
7421
+ // with `no such table`.
7422
+ statements: [],
7423
+ run: (tx) => {
7424
+ if (!tableExists(tx, "agent_sessions")) return;
7425
+ tx.run(sql2.raw(
7426
+ `UPDATE agent_sessions SET model_id = 'deepseek-ai/DeepSeek-V4-Flash' WHERE model_provider = 'deepinfra' AND model_id = 'zai-org/GLM-5.2'`
7427
+ ));
7428
+ }
7409
7429
  }
7410
7430
  ];
7411
7431
  function addRunsMeasurementPlanVersionForeignKey(tx) {
@@ -11345,6 +11365,22 @@ function requireAdminSession(request) {
11345
11365
  if (!role || role === UserRoles.admin) return;
11346
11366
  throw forbidden(ADMIN_ONLY_MESSAGE);
11347
11367
  }
11368
+ var NARROW_KEY_DENIED_MESSAGE = "This API key is narrower than the install, so it cannot use an administrator surface.";
11369
+ function isInstanceAdministrator(request) {
11370
+ const principal = request.principal;
11371
+ if (!principal) return true;
11372
+ const role = principal.kind === "user" ? principal.role : principal.delegatedUser?.role;
11373
+ if (role && role !== UserRoles.admin) return false;
11374
+ return !principal.projectId && principal.scopes.includes(WILDCARD_SCOPE);
11375
+ }
11376
+ function requireInstanceAdministrator(request) {
11377
+ const principal = request.principal;
11378
+ if (!principal) return;
11379
+ const role = principal.kind === "user" ? principal.role : principal.delegatedUser?.role;
11380
+ if (role && role !== UserRoles.admin) throw forbidden(ADMIN_ONLY_MESSAGE);
11381
+ if (isInstanceAdministrator(request)) return;
11382
+ throw forbidden(NARROW_KEY_DENIED_MESSAGE);
11383
+ }
11348
11384
  function requireOperator(request) {
11349
11385
  const principal = request.principal;
11350
11386
  if (request.operatorAccess === true && principal?.kind === "api-key" && !principal.viaCookie && !principal.projectId && !principal.delegatedUser) return;
@@ -42637,7 +42673,7 @@ var canonryLocalRouteCatalog = [
42637
42673
  method: "get",
42638
42674
  path: "/api/v1/projects/{name}/agent/transcript",
42639
42675
  summary: "Get the rolling Aero transcript for this project",
42640
- description: "Returns the full message history of the project-scoped Aero session plus the persisted model provider/id and last-updated timestamp. Empty messages array when the project has no session yet.",
42676
+ description: "Returns the full message history of the project-scoped Aero session plus the persisted model provider/id and last-updated timestamp. Empty messages array when the project has no session yet. Administrator-only: a signed-in viewer, and any API key narrower than the install (read-only, or scoped to a single project), are refused with 403. There is one Aero session per project, so this is the operator conversation rather than metadata about it.",
42641
42677
  tags: ["agent"],
42642
42678
  parameters: [nameParameter],
42643
42679
  responses: {
@@ -42650,7 +42686,7 @@ var canonryLocalRouteCatalog = [
42650
42686
  method: "delete",
42651
42687
  path: "/api/v1/projects/{name}/agent/transcript",
42652
42688
  summary: "Reset the Aero transcript + queued follow-ups",
42653
- description: "Evicts any live Agent instance, clears the persisted messages and follow_up_queue. A subsequent prompt starts a fresh session.",
42689
+ description: "Evicts any live Agent instance, clears the persisted messages and follow_up_queue. A subsequent prompt starts a fresh session. Administrator-only: a signed-in viewer, and any API key narrower than the install, are refused with 403.",
42654
42690
  tags: ["agent"],
42655
42691
  parameters: [nameParameter],
42656
42692
  responses: {
@@ -42663,7 +42699,7 @@ var canonryLocalRouteCatalog = [
42663
42699
  method: "get",
42664
42700
  path: "/api/v1/projects/{name}/agent/memory",
42665
42701
  summary: "List durable Aero memory entries for a project",
42666
- description: "Returns the project-scoped agent_memory rows newest-first. Includes both operator-authored notes (source `user`/`aero`) and LLM-authored compaction summaries (source `compaction`, key prefix `compaction:`). The N most-recent rows are also injected into the system prompt at every new session start.",
42702
+ description: "Returns the project-scoped agent_memory rows newest-first. Includes both operator-authored notes (source `user`/`aero`) and LLM-authored compaction summaries (source `compaction`, key prefix `compaction:`). The N most-recent rows are also injected into the system prompt at every new session start. Administrator-only: a signed-in viewer, and any API key narrower than the install, are refused with 403.",
42667
42703
  tags: ["agent"],
42668
42704
  parameters: [nameParameter],
42669
42705
  responses: {
@@ -42676,7 +42712,7 @@ var canonryLocalRouteCatalog = [
42676
42712
  method: "put",
42677
42713
  path: "/api/v1/projects/{name}/agent/memory",
42678
42714
  summary: "Upsert a durable Aero memory entry",
42679
- description: "Creates or replaces a project-scoped note (max 2 KB, max 128-char key). Same key replaces the prior value. Keys with the reserved `compaction:` prefix are rejected \u2014 that namespace is owned by transcript compaction.",
42715
+ description: "Creates or replaces a project-scoped note (max 2 KB, max 128-char key). Same key replaces the prior value. Keys with the reserved `compaction:` prefix are rejected \u2014 that namespace is owned by transcript compaction. Administrator-only: a signed-in viewer, and any API key narrower than the install, are refused with 403.",
42680
42716
  tags: ["agent"],
42681
42717
  parameters: [nameParameter],
42682
42718
  requestBody: {
@@ -42705,7 +42741,7 @@ var canonryLocalRouteCatalog = [
42705
42741
  method: "delete",
42706
42742
  path: "/api/v1/projects/{name}/agent/memory",
42707
42743
  summary: "Delete a durable Aero memory entry",
42708
- description: "Removes a single project-scoped note by key. Returns `status: missing` (non-error) when the key never existed. Keys with the reserved `compaction:` prefix are rejected \u2014 those notes are pruned automatically.",
42744
+ description: "Removes a single project-scoped note by key. Returns `status: missing` (non-error) when the key never existed. Keys with the reserved `compaction:` prefix are rejected \u2014 those notes are pruned automatically. Administrator-only: a signed-in viewer, and any API key narrower than the install, are refused with 403.",
42709
42745
  tags: ["agent"],
42710
42746
  parameters: [nameParameter],
42711
42747
  requestBody: {
@@ -42733,7 +42769,7 @@ var canonryLocalRouteCatalog = [
42733
42769
  method: "get",
42734
42770
  path: "/api/v1/projects/{name}/agent/providers",
42735
42771
  summary: "List the LLM providers Aero can route to",
42736
- description: "Returns every provider Aero knows about with its default model, whether a usable API key is configured, and where the key resolved from (`config` | `env`). `defaultProvider` is the one Aero auto-picks when a caller omits `provider` on the prompt endpoint. Path is project-scoped for auth symmetry; the response does not vary per project today.",
42772
+ description: "Returns every provider Aero knows about with its default model, whether a usable API key is configured, and where the key resolved from (`config` | `env`). `defaultProvider` is the one Aero auto-picks when a caller omits `provider` on the prompt endpoint. Path is project-scoped for auth symmetry; the response does not vary per project today. Administrator-only: a signed-in viewer, and any API key narrower than the install, are refused with 403, because each entry names a default model and which providers are configured is itself administrator knowledge.",
42737
42773
  tags: ["agent"],
42738
42774
  parameters: [nameParameter],
42739
42775
  responses: {
@@ -42745,7 +42781,7 @@ var canonryLocalRouteCatalog = [
42745
42781
  method: "post",
42746
42782
  path: "/api/v1/projects/{name}/agent/prompt",
42747
42783
  summary: "Send a prompt to Aero and stream events back as SSE",
42748
- description: 'Posts a prompt into the project\'s Aero session and streams `AgentEvent` frames as `text/event-stream`. Each frame is `data: <JSON>\\n\\n`. The server brackets the stream with `{"type":"stream_open"}` and `{"type":"stream_close"}` control frames; `{"type":"error","message":"..."}` surfaces in-stream failures without collapsing the stream. Returns 409 `AGENT_BUSY` if another turn is already in flight for this project. Body field `scope` accepts "all" | "read-only"; omitted defaults to "read-only" (safe dashboard surface). Body field `profile` accepts "default" | "ads-operator"; omitted keeps the default full Canonry operator surface. The CLI passes "all" to keep write tools available.',
42784
+ description: 'Posts a prompt into the project\'s Aero session and streams `AgentEvent` frames as `text/event-stream`. Each frame is `data: <JSON>\\n\\n`. The server brackets the stream with `{"type":"stream_open"}` and `{"type":"stream_close"}` control frames; `{"type":"error","message":"..."}` surfaces in-stream failures without collapsing the stream. Returns 409 `AGENT_BUSY` if another turn is already in flight for this project. Body field `scope` accepts "all" | "read-only"; omitted defaults to "read-only" (safe dashboard surface). Body field `profile` accepts "default" | "ads-operator"; omitted keeps the default full Canonry operator surface. The CLI passes "all" to keep write tools available. Administrator-only: a signed-in viewer, and any API key narrower than the install, are refused with 403. Tools execute with the install root key, so this route is an operator surface regardless of the scope requested.',
42749
42785
  tags: ["agent"],
42750
42786
  parameters: [nameParameter],
42751
42787
  requestBody: {
@@ -80704,6 +80740,14 @@ var agentProvidersConfiguredCheck = {
80704
80740
  scope: CheckScopes.global,
80705
80741
  title: "Agent provider keys",
80706
80742
  run: (ctx) => {
80743
+ if (ctx.callerIsInstanceAdministrator === false) {
80744
+ return {
80745
+ status: CheckStatuses.skipped,
80746
+ code: "agent-providers.restricted",
80747
+ summary: "Agent provider configuration is visible to administrators only.",
80748
+ remediation: null
80749
+ };
80750
+ }
80707
80751
  const summary2 = ctx.getAgentProviderSummary?.();
80708
80752
  if (!summary2) {
80709
80753
  return {
@@ -81970,6 +82014,7 @@ async function doctorRoutes(app, opts) {
81970
82014
  redirectUri,
81971
82015
  providerSummary: opts.providerSummary,
81972
82016
  getAgentProviderSummary: opts.getAgentProviderSummary,
82017
+ callerIsInstanceAdministrator: isInstanceAdministrator(request),
81973
82018
  trafficSourceValidators: opts.trafficSourceValidators,
81974
82019
  runtimeStatePaths: opts.runtimeStatePaths,
81975
82020
  bundledSkills: opts.bundledSkills,
@@ -82000,6 +82045,7 @@ async function doctorRoutes(app, opts) {
82000
82045
  redirectUri,
82001
82046
  providerSummary: opts.providerSummary,
82002
82047
  getAgentProviderSummary: opts.getAgentProviderSummary,
82048
+ callerIsInstanceAdministrator: isInstanceAdministrator(request),
82003
82049
  trafficSourceValidators: opts.trafficSourceValidators,
82004
82050
  runtimeStatePaths: opts.runtimeStatePaths,
82005
82051
  bundledSkills: opts.bundledSkills,
@@ -86753,6 +86799,7 @@ export {
86753
86799
  createUserSession,
86754
86800
  resolveUserSession,
86755
86801
  createCredentialChecker,
86802
+ requireInstanceAdministrator,
86756
86803
  hashApiKey,
86757
86804
  registerOAuthRoutes,
86758
86805
  registerOAuthAdminRoutes,
@@ -16,12 +16,12 @@ import {
16
16
  resolveOperatorApiKeyIds,
17
17
  runWithUsageTags,
18
18
  saveConfigPatch
19
- } from "./chunk-I4AHG7K3.js";
19
+ } from "./chunk-S4DBS5DP.js";
20
20
  import {
21
21
  deleteSiteCrawlGraphLayout,
22
22
  persistSiteCrawlGraphLayout,
23
23
  prepareSiteCrawlGraphLayout
24
- } from "./chunk-KZYOJ4F5.js";
24
+ } from "./chunk-R4COV5KR.js";
25
25
  import {
26
26
  PACKAGE_VERSION
27
27
  } from "./chunk-AOJU5LKX.js";
@@ -152,6 +152,7 @@ import {
152
152
  refreshAccessToken,
153
153
  registerOAuthAdminRoutes,
154
154
  registerOAuthRoutes,
155
+ requireInstanceAdministrator,
155
156
  researchRunQueries,
156
157
  researchRuns,
157
158
  resolveDestination,
@@ -176,7 +177,7 @@ import {
176
177
  siteLivenessState,
177
178
  toAlertView,
178
179
  usageCounters
179
- } from "./chunk-TM2NQMJA.js";
180
+ } from "./chunk-5E4ASNU4.js";
180
181
  import {
181
182
  AGENT_MEMORY_VALUE_MAX_BYTES,
182
183
  AGENT_NONE,
@@ -6040,6 +6041,13 @@ function resolveAgentEnabled(env, config) {
6040
6041
  }
6041
6042
  return config.agent?.mode !== "disabled";
6042
6043
  }
6044
+ function resolveAgentProactiveEnabled(env, config) {
6045
+ const raw = env.CANONRY_AGENT_PROMPT_ONLY?.trim();
6046
+ if (raw) {
6047
+ return !(raw === "1" || raw.toLowerCase() === "true");
6048
+ }
6049
+ return config.agent?.mode !== "prompt-only";
6050
+ }
6043
6051
 
6044
6052
  // src/places-config.ts
6045
6053
  var DEFAULT_TIER = "atmosphere";
@@ -6468,7 +6476,7 @@ function nonBlank(value) {
6468
6476
  return trimmed ? trimmed : void 0;
6469
6477
  }
6470
6478
  function resolveBuildCommit(env = process.env) {
6471
- const embedded = true ? "f2d9ae1712b6aa64f5f6d4141c9a5208763a634e" : void 0;
6479
+ const embedded = true ? "6009ba08439de17b373d8958bae95b4794f02af0" : void 0;
6472
6480
  return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
6473
6481
  }
6474
6482
  function resolveInstanceIdentity(env = process.env) {
@@ -11762,7 +11770,7 @@ function readStoredGroundingSources(rawResponse) {
11762
11770
  return result;
11763
11771
  }
11764
11772
  async function backfillInsightsCommand(project, opts) {
11765
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-5P62KKG6.js");
11773
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-4XMMO3JK.js");
11766
11774
  const config = loadConfig();
11767
11775
  const db = createClient(config.database);
11768
11776
  migrate(db);
@@ -13656,11 +13664,18 @@ var PROVIDER_MODELS = {
13656
13664
  [LlmCapabilities.classify]: "glm-5-turbo"
13657
13665
  },
13658
13666
  [AgentProviderIds.deepinfra]: {
13659
- // DeepInfra `org/Model` slugs. GLM-5.2 (Sonnet-class) on all three tiers —
13660
- // it undercuts the Claude tiers it replaces, so the cheaper tiers take no
13661
- // quality hit. Resolved into a custom openai-completions model, not pi-ai's
13662
- // catalog.
13663
- [LlmCapabilities.agent]: "zai-org/GLM-5.2",
13667
+ // DeepInfra `org/Model` slugs, resolved into a custom openai-completions
13668
+ // model rather than pi-ai's catalog.
13669
+ //
13670
+ // The agent tier is pinned explicitly: the dashboard picker sends a
13671
+ // provider and no model, so whatever sits here is what selecting DeepInfra
13672
+ // pins a session to.
13673
+ //
13674
+ // analyze + classify stay on GLM on purpose. Those tiers suppress the
13675
+ // reasoning trace through GLM's chat-template switch, and that branch
13676
+ // applies only to a model declared `reasoning: true`; moving them to
13677
+ // DeepSeek-V4-Flash (which is not) would quietly stop suppressing anything.
13678
+ [LlmCapabilities.agent]: "deepseek-ai/DeepSeek-V4-Flash",
13664
13679
  [LlmCapabilities.analyze]: "zai-org/GLM-5.2",
13665
13680
  [LlmCapabilities.classify]: "zai-org/GLM-5.2"
13666
13681
  }
@@ -13718,7 +13733,7 @@ var AGENT_PROVIDERS = {
13718
13733
  },
13719
13734
  // Best-effort metadata. Costs are USD/1M tokens (DeepInfra published
13720
13735
  // rates: GLM-5.2 ~$0.95 in / $0.18 cached / $3.00 out; DeepSeek-V4-Flash
13721
- // ~$0.10 in / $0.20 out). contextWindow is DeepInfra's 1M (fp4) serving
13736
+ // $0.09 in / $0.18 out). contextWindow is DeepInfra's 1M (fp4) serving
13722
13737
  // window for both models; it's descriptive (see OpenAiCompatibleModelMeta),
13723
13738
  // so it documents the real window rather than gating compaction.
13724
13739
  knownModels: {
@@ -13732,7 +13747,7 @@ var AGENT_PROVIDERS = {
13732
13747
  contextWindow: 1048576,
13733
13748
  maxTokens: 32768,
13734
13749
  reasoning: false,
13735
- cost: { input: 0.1, output: 0.2, cacheRead: 0, cacheWrite: 0 }
13750
+ cost: { input: 0.09, output: 0.18, cacheRead: 0, cacheWrite: 0 }
13736
13751
  }
13737
13752
  },
13738
13753
  // Fallback for arbitrary `--model` slugs we don't ship as tiers. cost is
@@ -15065,6 +15080,10 @@ var SessionRegistry = class {
15065
15080
  constructor(opts) {
15066
15081
  this.opts = opts;
15067
15082
  }
15083
+ /** False only in prompt-only mode; absent means proactive, as it always was. */
15084
+ get proactive() {
15085
+ return this.opts.proactive !== false;
15086
+ }
15068
15087
  /**
15069
15088
  * Lazily load + cache the injected remote MCP tools. The seam OSS-A exposes:
15070
15089
  * `loadExternalMcpTools` connects to each configured server over the frozen
@@ -15149,7 +15168,7 @@ var SessionRegistry = class {
15149
15168
  this.scopes.set(projectName, preferences?.toolScope ?? AeroToolScopes.all);
15150
15169
  this.profiles.set(projectName, preferences?.toolProfile ?? AeroToolProfiles.default);
15151
15170
  this.projectIds.set(projectName, projectId);
15152
- if (queued.length > 0) {
15171
+ if (queued.length > 0 && this.proactive) {
15153
15172
  this.appendPending(projectName, queued);
15154
15173
  this.updateRow(projectId, { followUpQueue: "[]" });
15155
15174
  }
@@ -15395,6 +15414,7 @@ ${lines.join("\n")}
15395
15414
  * both the in-memory pending and the DB-queue migration produced copies).
15396
15415
  */
15397
15416
  queueFollowUp(projectName, message) {
15417
+ if (!this.proactive) return;
15398
15418
  if (this.live.has(projectName)) {
15399
15419
  this.appendPending(projectName, [message]);
15400
15420
  } else {
@@ -15403,6 +15423,7 @@ ${lines.join("\n")}
15403
15423
  }
15404
15424
  /** Consume (and clear) the pending queue for a project. Caller prompts with the result. */
15405
15425
  consumePending(projectName) {
15426
+ if (!this.proactive) return [];
15406
15427
  const msgs = this.pending.get(projectName) ?? [];
15407
15428
  if (msgs.length === 0) return [];
15408
15429
  this.pending.delete(projectName);
@@ -15422,6 +15443,7 @@ ${lines.join("\n")}
15422
15443
  * RunCoordinator calls after a run completes to wake Aero unprompted.
15423
15444
  */
15424
15445
  async drainNow(projectName) {
15446
+ if (!this.proactive) return;
15425
15447
  if (!this.hasPendingWork(projectName)) return;
15426
15448
  try {
15427
15449
  let agent;
@@ -15786,13 +15808,15 @@ function registerAgentRoutes(app, opts) {
15786
15808
  app.get(
15787
15809
  "/projects/:name/agent/transcript",
15788
15810
  async (request) => {
15811
+ requireInstanceAdministrator(request);
15789
15812
  const project = resolveProject(opts.db, request.params.name);
15790
15813
  const row = opts.db.select().from(agentSessions).where(eq22(agentSessions.projectId, project.id)).get();
15791
15814
  if (!row) {
15792
15815
  return { messages: [], modelProvider: null, modelId: null, updatedAt: null };
15793
15816
  }
15817
+ const messages = parseJsonColumn(row.messages, []);
15794
15818
  return {
15795
- messages: parseJsonColumn(row.messages, []),
15819
+ messages,
15796
15820
  modelProvider: row.modelProvider,
15797
15821
  modelId: row.modelId,
15798
15822
  updatedAt: row.updatedAt
@@ -15802,6 +15826,7 @@ function registerAgentRoutes(app, opts) {
15802
15826
  app.get(
15803
15827
  "/projects/:name/agent/providers",
15804
15828
  async (request) => {
15829
+ requireInstanceAdministrator(request);
15805
15830
  resolveProject(opts.db, request.params.name);
15806
15831
  return buildAgentProvidersResponse(opts.sessionRegistry.getConfig());
15807
15832
  }
@@ -15809,6 +15834,7 @@ function registerAgentRoutes(app, opts) {
15809
15834
  app.delete(
15810
15835
  "/projects/:name/agent/transcript",
15811
15836
  async (request) => {
15837
+ requireInstanceAdministrator(request);
15812
15838
  const project = resolveProject(opts.db, request.params.name);
15813
15839
  opts.sessionRegistry.reset(project.name);
15814
15840
  opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq22(agentSessions.projectId, project.id)).run();
@@ -15816,6 +15842,7 @@ function registerAgentRoutes(app, opts) {
15816
15842
  }
15817
15843
  );
15818
15844
  app.post("/projects/:name/agent/prompt", async (request, reply) => {
15845
+ requireInstanceAdministrator(request);
15819
15846
  const project = resolveProject(opts.db, request.params.name);
15820
15847
  const body = request.body;
15821
15848
  const promptText = (body?.prompt ?? "").trim();
@@ -15877,6 +15904,7 @@ function registerAgentRoutes(app, opts) {
15877
15904
  app.get(
15878
15905
  "/projects/:name/agent/memory",
15879
15906
  async (request) => {
15907
+ requireInstanceAdministrator(request);
15880
15908
  const project = resolveProject(opts.db, request.params.name);
15881
15909
  return { entries: listMemoryEntries(opts.db, project.id) };
15882
15910
  }
@@ -15884,6 +15912,7 @@ function registerAgentRoutes(app, opts) {
15884
15912
  app.put(
15885
15913
  "/projects/:name/agent/memory",
15886
15914
  async (request) => {
15915
+ requireInstanceAdministrator(request);
15887
15916
  const project = resolveProject(opts.db, request.params.name);
15888
15917
  const parsed = agentMemoryUpsertRequestSchema.safeParse(request.body);
15889
15918
  if (!parsed.success) {
@@ -15910,6 +15939,7 @@ function registerAgentRoutes(app, opts) {
15910
15939
  app.delete(
15911
15940
  "/projects/:name/agent/memory",
15912
15941
  async (request) => {
15942
+ requireInstanceAdministrator(request);
15913
15943
  const project = resolveProject(opts.db, request.params.name);
15914
15944
  const parsed = agentMemoryDeleteRequestSchema.safeParse(request.body);
15915
15945
  if (!parsed.success) {
@@ -17597,10 +17627,12 @@ async function createServer(opts) {
17597
17627
  skipProbe: true
17598
17628
  });
17599
17629
  const agentEnabled = resolveAgentEnabled(process.env, opts.config);
17630
+ const agentProactive = resolveAgentProactiveEnabled(process.env, opts.config);
17600
17631
  const sessionRegistry = agentEnabled ? new SessionRegistry({
17601
17632
  db: opts.db,
17602
17633
  client: aeroClient,
17603
- config: opts.config
17634
+ config: opts.config,
17635
+ proactive: agentProactive
17604
17636
  }) : void 0;
17605
17637
  const runCoordinator = new RunCoordinator(
17606
17638
  opts.db,
@@ -17609,6 +17641,7 @@ async function createServer(opts) {
17609
17641
  (runId, projectId, result) => notifier.dispatchInsightWebhooks(runId, projectId, result),
17610
17642
  async (ctx) => {
17611
17643
  if (!sessionRegistry) return;
17644
+ if (!agentProactive) return;
17612
17645
  const project = opts.db.select({ name: projects.name }).from(projects).where(eq25(projects.id, ctx.projectId)).get();
17613
17646
  if (!project) return;
17614
17647
  let content;
@@ -4,7 +4,7 @@ import {
4
4
  installSkills,
5
5
  loadConfigRaw,
6
6
  saveConfigPatch
7
- } from "./chunk-I4AHG7K3.js";
7
+ } from "./chunk-S4DBS5DP.js";
8
8
  import {
9
9
  PACKAGE_VERSION
10
10
  } from "./chunk-AOJU5LKX.js";
@@ -5,7 +5,7 @@ import {
5
5
  siteCrawlGraphNodes,
6
6
  siteCrawlPages,
7
7
  siteCrawlSnapshots
8
- } from "./chunk-TM2NQMJA.js";
8
+ } from "./chunk-5E4ASNU4.js";
9
9
  import {
10
10
  SITE_CRAWL_GRAPH_MAX_EDGES,
11
11
  SITE_CRAWL_GRAPH_MAX_NODES,
@@ -10041,7 +10041,7 @@ var canonryMcpTools = [
10041
10041
  defineTool({
10042
10042
  name: "canonry_project_overview",
10043
10043
  title: "Get project overview (composite)",
10044
- description: 'One-call summary for "how is project X doing?". Returns independent mention and citation coverage, separate query-level movement for each signal, query-basket comparability with added/removed counts, latest run and health, insights, provider/model breakdowns, competitors, attention items, and recent history. Movement excludes queries not shared by both sweeps. Filterable by location and time window. Prefer this over fanning out to separate tools.',
10044
+ description: 'One-call summary for "how is project X doing?". Returns independent mention and citation coverage, separate query-level movement for each signal, query-basket comparability with added/removed counts, latest run and health, insights, provider/model breakdowns, competitors, attention items, and recent history. Movement excludes queries not shared by both sweeps. Filterable by location and time window. Prefer this over fanning out to separate read tools for the same question. SCOPE LIMIT: this returns nothing about Advanced Measurement: no plan, Targets, Properties, groups, or segments. Its silence is not evidence that a project lacks them. Before stating that a project has no Properties, no Advanced plan, or cannot break out per-Property performance, call canonry_measurement_plan_get to establish the portfolio type, and canonry_measurement_portfolio_summary to rank Properties.',
10045
10045
  access: "read",
10046
10046
  tier: "core",
10047
10047
  inputSchema: z4.object({
package/dist/cli.js CHANGED
@@ -35,11 +35,11 @@ import {
35
35
  trackCliCommandFinished,
36
36
  trackEvent,
37
37
  waitForServerRuntimeStartup
38
- } from "./chunk-C4KBR4H6.js";
38
+ } from "./chunk-BO53QARL.js";
39
39
  import {
40
40
  autoSyncSkills,
41
41
  formatAutoSyncNotice
42
- } from "./chunk-GHAUT7XJ.js";
42
+ } from "./chunk-LR7BVM53.js";
43
43
  import {
44
44
  CliError,
45
45
  EXIT_SYSTEM_ERROR,
@@ -66,13 +66,13 @@ import {
66
66
  shouldPrintServeSkillsNudge,
67
67
  systemError,
68
68
  usageError
69
- } from "./chunk-I4AHG7K3.js";
69
+ } from "./chunk-S4DBS5DP.js";
70
70
  import {
71
71
  DEFAULT_DEMO_TRUSTED_PROXIES,
72
72
  trustedProxiesProblem,
73
73
  trustedProxyProblem
74
74
  } from "./chunk-RB2F4D3Z.js";
75
- import "./chunk-KZYOJ4F5.js";
75
+ import "./chunk-R4COV5KR.js";
76
76
  import "./chunk-AOJU5LKX.js";
77
77
  import {
78
78
  CLOUDFLARE_WORKER_BINDINGS,
@@ -88,7 +88,7 @@ import {
88
88
  queries,
89
89
  renderReportHtml,
90
90
  runs
91
- } from "./chunk-TM2NQMJA.js";
91
+ } from "./chunk-5E4ASNU4.js";
92
92
  import {
93
93
  AdsDeliverySnapshotStatuses,
94
94
  AdsHistoricalCampaignRollupStatuses,
@@ -435,7 +435,7 @@ function parseDemoTrustedProxies(values) {
435
435
  async function demoCommand(options) {
436
436
  const { host, port } = parseDemoListenOptions(options);
437
437
  const trustProxy = parseDemoTrustedProxies(options.trustProxy);
438
- const { createDemoServer } = await import("./demo-server-JEGVGOPQ.js");
438
+ const { createDemoServer } = await import("./demo-server-WRVVNKLS.js");
439
439
  const app = await createDemoServer({ trustProxy });
440
440
  let stopping = false;
441
441
  const stop = () => {
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  layoutSiteCrawlGraphInput,
6
6
  persistSiteCrawlGraphLayout
7
- } from "./chunk-KZYOJ4F5.js";
7
+ } from "./chunk-R4COV5KR.js";
8
8
  import {
9
9
  PACKAGE_VERSION
10
10
  } from "./chunk-AOJU5LKX.js";
@@ -83,7 +83,7 @@ import {
83
83
  siteCrawlPages,
84
84
  siteCrawlSnapshots,
85
85
  trafficSources
86
- } from "./chunk-TM2NQMJA.js";
86
+ } from "./chunk-5E4ASNU4.js";
87
87
  import {
88
88
  RunKinds,
89
89
  SiteCrawlFetchStates,
package/dist/index.d.ts CHANGED
@@ -268,15 +268,27 @@ interface ExternalMcpServerConfig {
268
268
  }
269
269
  interface AgentConfigEntry {
270
270
  /**
271
- * Agent mode. `'disabled'` turns the built-in Aero agent OFF entirely the
272
- * proactive auto-wake on run completion does not fire, the `SessionRegistry`
273
- * is not constructed, and the interactive agent routes (`/projects/:name/
274
- * agent/*`) plus the `canonry agent ask` CLI (a thin client of those routes)
275
- * are not served. Absent (the default) leaves Aero enabled. Resolved, with
276
- * the `CANONRY_AGENT_DISABLED` env override, by `resolveAgentEnabled` in
277
- * agent-config.ts.
271
+ * Agent mode. Three states, held on ONE field so they stay mutually
272
+ * exclusive: there is no way to write down "off, but still waking itself".
273
+ *
274
+ * - `'disabled'` turns the built-in Aero agent OFF entirely the proactive
275
+ * auto-wake on run completion does not fire, the `SessionRegistry` is not
276
+ * constructed, and the interactive agent routes (`/projects/:name/agent/*`)
277
+ * plus the `canonry agent ask` CLI (a thin client of those routes) are not
278
+ * served. Resolved, with the `CANONRY_AGENT_DISABLED` env override, by
279
+ * `resolveAgentEnabled`.
280
+ * - `'prompt-only'` keeps every interactive surface and removes ONLY the
281
+ * proactive wake: Aero answers when asked and never starts a turn by
282
+ * itself, so it costs nothing while nobody is talking to it and never
283
+ * writes into the transcript unattended. Resolved, with the
284
+ * `CANONRY_AGENT_PROMPT_ONLY` env override, by
285
+ * `resolveAgentProactiveEnabled`.
286
+ * - absent (the default) leaves Aero enabled AND proactive, which is the
287
+ * behaviour every existing install already has.
288
+ *
289
+ * Both resolvers live in agent-config.ts.
278
290
  */
279
- mode?: 'disabled';
291
+ mode?: 'disabled' | 'prompt-only';
280
292
  }
281
293
  interface DashboardConfigEntry {
282
294
  /**
package/dist/index.js CHANGED
@@ -3,13 +3,13 @@ import {
3
3
  createGoogleMarketingCredentialStore,
4
4
  createGoogleMarketingRuntime,
5
5
  createServer
6
- } from "./chunk-C4KBR4H6.js";
6
+ } from "./chunk-BO53QARL.js";
7
7
  import {
8
8
  loadConfig
9
- } from "./chunk-I4AHG7K3.js";
10
- import "./chunk-KZYOJ4F5.js";
9
+ } from "./chunk-S4DBS5DP.js";
10
+ import "./chunk-R4COV5KR.js";
11
11
  import "./chunk-AOJU5LKX.js";
12
- import "./chunk-TM2NQMJA.js";
12
+ import "./chunk-5E4ASNU4.js";
13
13
  import "./chunk-D3DKDTXP.js";
14
14
  export {
15
15
  GoogleMarketingRuntimeError,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  HISTORY_WINDOW_RUNS,
3
3
  IntelligenceService
4
- } from "./chunk-TM2NQMJA.js";
4
+ } from "./chunk-5E4ASNU4.js";
5
5
  import "./chunk-D3DKDTXP.js";
6
6
  export {
7
7
  HISTORY_WINDOW_RUNS,
package/dist/mcp.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  autoSyncSkills
3
- } from "./chunk-GHAUT7XJ.js";
3
+ } from "./chunk-LR7BVM53.js";
4
4
  import {
5
5
  createApiClient,
6
6
  createCanonryMcpServer
7
- } from "./chunk-I4AHG7K3.js";
7
+ } from "./chunk-S4DBS5DP.js";
8
8
  import "./chunk-AOJU5LKX.js";
9
9
  import {
10
10
  isReadOnlyKey,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainyc/canonry",
3
- "version": "5.6.0",
3
+ "version": "5.8.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": [
@@ -86,32 +86,32 @@
86
86
  "@types/node-cron": "^3.0.11",
87
87
  "tsup": "^8.5.1",
88
88
  "tsx": "^4.19.0",
89
+ "@ainyc/canonry-api-routes": "0.0.0",
89
90
  "@ainyc/canonry-web": "0.0.0",
90
- "@ainyc/canonry-api-client": "0.0.0",
91
91
  "@ainyc/canonry-config": "0.0.0",
92
+ "@ainyc/canonry-api-client": "0.0.0",
92
93
  "@ainyc/canonry-contracts": "0.0.0",
93
- "@ainyc/canonry-api-routes": "0.0.0",
94
94
  "@ainyc/canonry-db": "0.0.0",
95
- "@ainyc/canonry-integration-cloud-run": "0.0.0",
96
- "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
97
95
  "@ainyc/canonry-integration-bing": "0.0.0",
98
96
  "@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
99
97
  "@ainyc/canonry-integration-commoncrawl": "0.0.0",
98
+ "@ainyc/canonry-integration-cloud-run": "0.0.0",
100
99
  "@ainyc/canonry-integration-google": "0.0.0",
100
+ "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
101
+ "@ainyc/canonry-integration-google-ads": "0.0.0",
101
102
  "@ainyc/canonry-integration-google-business-profile": "0.0.0",
102
103
  "@ainyc/canonry-integration-google-places": "0.0.0",
103
- "@ainyc/canonry-integration-traffic": "0.0.0",
104
- "@ainyc/canonry-integration-google-ads": "0.0.0",
105
- "@ainyc/canonry-integration-wordpress": "0.0.0",
106
- "@ainyc/canonry-integration-openai-ads": "0.0.0",
107
104
  "@ainyc/canonry-integration-google-tag-manager": "0.0.0",
105
+ "@ainyc/canonry-integration-openai-ads": "0.0.0",
106
+ "@ainyc/canonry-integration-wordpress": "0.0.0",
107
+ "@ainyc/canonry-integration-traffic": "0.0.0",
108
108
  "@ainyc/canonry-intelligence": "0.0.0",
109
109
  "@ainyc/canonry-provider-cdp": "0.0.0",
110
110
  "@ainyc/canonry-provider-claude": "0.0.0",
111
- "@ainyc/canonry-provider-openai": "0.0.0",
112
111
  "@ainyc/canonry-provider-local": "0.0.0",
112
+ "@ainyc/canonry-provider-gemini": "0.0.0",
113
113
  "@ainyc/canonry-provider-perplexity": "0.0.0",
114
- "@ainyc/canonry-provider-gemini": "0.0.0"
114
+ "@ainyc/canonry-provider-openai": "0.0.0"
115
115
  },
116
116
  "scripts": {
117
117
  "build": "pnpm run build:cli && pnpm run build:web",