@ainyc/canonry 4.100.1 → 4.102.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 (24) hide show
  1. package/README.md +4 -1
  2. package/assets/assets/{BacklinksPage-v2RC8wlg.js → BacklinksPage-lXt19SLR.js} +1 -1
  3. package/assets/assets/{ChartPrimitives-B74e5pqn.js → ChartPrimitives-C-sLgrDV.js} +1 -1
  4. package/assets/assets/ProjectPage-wtvaWIRA.js +7 -0
  5. package/assets/assets/{RunRow-BmVPjO-m.js → RunRow-Db32k-uH.js} +1 -1
  6. package/assets/assets/{RunsPage-CK5atXvL.js → RunsPage-DwW3Mely.js} +1 -1
  7. package/assets/assets/{SettingsPage-DarPFUPl.js → SettingsPage-gXn6OGsu.js} +1 -1
  8. package/assets/assets/{TrafficPage-Ddnn_WqU.js → TrafficPage-I49ci4gj.js} +1 -1
  9. package/assets/assets/{TrafficSourceDetailPage-g08SKnwZ.js → TrafficSourceDetailPage-BCvLztx9.js} +1 -1
  10. package/assets/assets/{arrow-left-D39Vxasb.js → arrow-left-BpA06YY0.js} +1 -1
  11. package/assets/assets/{extract-error-message-BiNWqBeh.js → extract-error-message-BZSIQ5iV.js} +1 -1
  12. package/assets/assets/{index-Dd4J1VhB.js → index-DSSLug80.js} +64 -64
  13. package/assets/assets/{trash-2-xh6gsnap.js → trash-2-CLkUNdvb.js} +1 -1
  14. package/assets/index.html +1 -1
  15. package/dist/{chunk-IDI6LMYL.js → chunk-2FUODJQR.js} +6 -2
  16. package/dist/{chunk-ALJSES3X.js → chunk-3PVCILSN.js} +565 -51
  17. package/dist/{chunk-YBIT5O62.js → chunk-B2H2RRIT.js} +927 -614
  18. package/dist/{chunk-JF7EXHGA.js → chunk-EOQXB63S.js} +30 -2
  19. package/dist/cli.js +44 -11
  20. package/dist/index.js +4 -4
  21. package/dist/{intelligence-service-TWCEKVKZ.js → intelligence-service-RC4BY5HF.js} +2 -2
  22. package/dist/mcp.js +2 -2
  23. package/package.json +10 -10
  24. package/assets/assets/ProjectPage-BCzS7TQn.js +0 -6
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  ApiClient,
3
+ CanonryMcpToolNames,
3
4
  CliError,
4
5
  PACKAGE_VERSION,
5
6
  canonryMcpTools,
@@ -9,7 +10,7 @@ import {
9
10
  loadConfig,
10
11
  loadConfigRaw,
11
12
  saveConfigPatch
12
- } from "./chunk-IDI6LMYL.js";
13
+ } from "./chunk-2FUODJQR.js";
13
14
  import {
14
15
  CC_CACHE_DIR,
15
16
  DUCKDB_SPEC,
@@ -24,6 +25,7 @@ import {
24
25
  adsInsightsDaily,
25
26
  agentMemory,
26
27
  agentSessions,
28
+ agentToolEvents,
27
29
  aiUserFetchEventsHourly,
28
30
  apiKeys,
29
31
  apiRoutes,
@@ -74,6 +76,7 @@ import {
74
76
  getUrlLinks,
75
77
  groupRunsByCreatedAt,
76
78
  gscCoverageSnapshots,
79
+ gscDailyTotals,
77
80
  gscSearchData,
78
81
  gscUrlInspections,
79
82
  hashAttributes,
@@ -88,6 +91,7 @@ import {
88
91
  listLocations,
89
92
  listMonthlyKeywords,
90
93
  listPlaceActionLinks,
94
+ llmUsageEvents,
91
95
  loadDuckdb,
92
96
  markSessionFailed,
93
97
  migrate,
@@ -111,7 +115,7 @@ import {
111
115
  siteAuditPages,
112
116
  siteAuditSnapshots,
113
117
  usageCounters
114
- } from "./chunk-YBIT5O62.js";
118
+ } from "./chunk-B2H2RRIT.js";
115
119
  import {
116
120
  AGENT_MEMORY_VALUE_MAX_BYTES,
117
121
  AGENT_PROVIDER_IDS,
@@ -159,6 +163,7 @@ import {
159
163
  hostOf,
160
164
  isAgentProviderId,
161
165
  isBrowserProvider,
166
+ isGhostTelemetryEvent,
162
167
  isRetryableHttpError,
163
168
  normalizeUrlPath,
164
169
  notFound,
@@ -171,7 +176,7 @@ import {
171
176
  validationError,
172
177
  winnabilityClassLabel,
173
178
  withRetry
174
- } from "./chunk-JF7EXHGA.js";
179
+ } from "./chunk-EOQXB63S.js";
175
180
 
176
181
  // src/telemetry.ts
177
182
  import crypto from "crypto";
@@ -179,10 +184,13 @@ import os from "os";
179
184
  import { createRequire } from "module";
180
185
  var _require = createRequire(import.meta.url);
181
186
  var { version: VERSION } = _require("../package.json");
182
- var TELEMETRY_ENDPOINT = "https://ainyc.ai/api/telemetry";
187
+ var TELEMETRY_ENDPOINT = "https://canonry.ai/api/telemetry";
183
188
  var TIMEOUT_MS = 3e3;
184
189
  var ANON_ID_ENV_VAR = "CANONRY_ANONYMOUS_ID";
185
190
  var ANON_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
191
+ function shouldDropTelemetryEvent(event, properties) {
192
+ return isGhostTelemetryEvent(event, properties);
193
+ }
186
194
  var SESSION_ID = crypto.randomUUID();
187
195
  var CURRENT_SOURCE = "cli";
188
196
  function setTelemetrySource(source) {
@@ -272,7 +280,7 @@ function isFirstRun() {
272
280
  }
273
281
  function showFirstRunNotice() {
274
282
  process.stderr.write(
275
- "\nCanonry collects anonymous telemetry to prioritize features.\nDisable any time: canonry telemetry disable\nLearn more: https://ainyc.ai/telemetry\n\n"
283
+ "\nCanonry collects anonymous telemetry to prioritize features.\nDisable any time: canonry telemetry disable\nLearn more: https://canonry.ai/telemetry\n\n"
276
284
  );
277
285
  }
278
286
  function detectAndTrackUpgrade() {
@@ -296,6 +304,7 @@ function detectAndTrackUpgrade() {
296
304
  }
297
305
  function trackEvent(event, properties, options) {
298
306
  if (!isTelemetryEnabled()) return;
307
+ if (shouldDropTelemetryEvent(event, properties)) return;
299
308
  const anonymousId = getOrCreateAnonymousId();
300
309
  if (!anonymousId) return;
301
310
  const payload = {
@@ -459,7 +468,7 @@ function checkLatestVersionForServer(opts) {
459
468
 
460
469
  // src/server.ts
461
470
  import { createRequire as createRequire3 } from "module";
462
- import crypto20 from "crypto";
471
+ import crypto22 from "crypto";
463
472
  import fs8 from "fs";
464
473
  import path9 from "path";
465
474
  import { fileURLToPath as fileURLToPath3 } from "url";
@@ -3790,6 +3799,32 @@ async function executeGscSync(db, runId, projectId, opts) {
3790
3799
  }).run();
3791
3800
  }
3792
3801
  }
3802
+ const totalRows = await fetchSearchAnalytics(accessToken, propertyId, {
3803
+ startDate,
3804
+ endDate,
3805
+ dimensions: ["date"]
3806
+ });
3807
+ db.delete(gscDailyTotals).where(
3808
+ and2(
3809
+ eq2(gscDailyTotals.projectId, projectId),
3810
+ sql2`${gscDailyTotals.date} >= ${startDate}`,
3811
+ sql2`${gscDailyTotals.date} <= ${endDate}`
3812
+ )
3813
+ ).run();
3814
+ const dailyTotalsNow = (/* @__PURE__ */ new Date()).toISOString();
3815
+ for (const row of totalRows) {
3816
+ const [date] = row.keys;
3817
+ db.insert(gscDailyTotals).values({
3818
+ id: crypto4.randomUUID(),
3819
+ projectId,
3820
+ date: date ?? "",
3821
+ clicks: row.clicks,
3822
+ impressions: row.impressions,
3823
+ position: String(row.position),
3824
+ createdAt: dailyTotalsNow
3825
+ }).run();
3826
+ }
3827
+ log2.info("daily-totals.complete", { runId, projectId, rowCount: totalRows.length });
3793
3828
  const pageClicks = /* @__PURE__ */ new Map();
3794
3829
  for (const row of rows) {
3795
3830
  const page = row.keys[1];
@@ -6403,7 +6438,7 @@ function readStoredGroundingSources(rawResponse) {
6403
6438
  return result;
6404
6439
  }
6405
6440
  async function backfillInsightsCommand(project, opts) {
6406
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-TWCEKVKZ.js");
6441
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-RC4BY5HF.js");
6407
6442
  const config = loadConfig();
6408
6443
  const db = createClient(config.database);
6409
6444
  migrate(db);
@@ -7979,7 +8014,7 @@ function analysisResultFromInsights(insights2) {
7979
8014
  }
7980
8015
 
7981
8016
  // src/agent/session-registry.ts
7982
- import crypto19 from "crypto";
8017
+ import crypto21 from "crypto";
7983
8018
  import { eq as eq18 } from "drizzle-orm";
7984
8019
 
7985
8020
  // src/agent/session.ts
@@ -8140,6 +8175,11 @@ function resolveModelForCapability(provider, capability, modelIdOverride) {
8140
8175
  `Model '${id}' not found for pi-ai provider '${entry.piAiProvider}'. Verify PROVIDER_MODELS[${provider}][${capability}] against the installed @mariozechner/pi-ai catalog.`
8141
8176
  );
8142
8177
  }
8178
+ if (provider === AgentProviderIds.gemini && process.env.GEMINI_BASE_URL) {
8179
+ const base = process.env.GEMINI_BASE_URL.replace(/\/+$/, "");
8180
+ const baseUrl = base.endsWith("/v1beta") ? base : `${base}/v1beta`;
8181
+ return { ...model, baseUrl };
8182
+ }
8143
8183
  return model;
8144
8184
  }
8145
8185
  function buildOpenAiCompatibleModel(entry, id, suppressThinking = false) {
@@ -8332,6 +8372,9 @@ function buildSkillDocTools() {
8332
8372
  ];
8333
8373
  }
8334
8374
 
8375
+ // src/agent/tools.ts
8376
+ import { Type as Type3 } from "@sinclair/typebox";
8377
+
8335
8378
  // src/agent/mcp-to-agent-tool.ts
8336
8379
  import { Type as Type2 } from "@sinclair/typebox";
8337
8380
  var MAX_TOOL_RESULT_CHARS = 2e4;
@@ -8431,19 +8474,431 @@ function mcpToAgentTool(tool, ctx) {
8431
8474
  };
8432
8475
  }
8433
8476
  var AERO_EXCLUDED_MCP_TOOLS = /* @__PURE__ */ new Set([
8434
- "canonry_agent_clear"
8477
+ CanonryMcpToolNames.canonry_agent_clear
8435
8478
  ]);
8436
8479
  function buildMcpAgentTools(registry, ctx, opts = {}) {
8437
- return registry.filter((tool) => !AERO_EXCLUDED_MCP_TOOLS.has(tool.name)).filter((tool) => opts.readOnly ? tool.access === "read" : true).map((tool) => mcpToAgentTool(tool, ctx));
8480
+ return registry.filter((tool) => !AERO_EXCLUDED_MCP_TOOLS.has(tool.name)).filter((tool) => opts.includeNames ? opts.includeNames.has(tool.name) : true).filter((tool) => opts.readOnly ? tool.access === "read" : true).map((tool) => mcpToAgentTool(tool, ctx));
8438
8481
  }
8439
8482
 
8440
8483
  // src/agent/tools.ts
8484
+ var AeroToolScopes = Object.freeze({
8485
+ all: "all",
8486
+ readOnly: "read-only"
8487
+ });
8488
+ var AERO_TOOL_SCOPES = [
8489
+ AeroToolScopes.all,
8490
+ AeroToolScopes.readOnly
8491
+ ];
8492
+ var AeroToolProfiles = Object.freeze({
8493
+ default: "default",
8494
+ adsOperator: "ads-operator"
8495
+ });
8496
+ var AERO_TOOL_PROFILES = [
8497
+ AeroToolProfiles.default,
8498
+ AeroToolProfiles.adsOperator
8499
+ ];
8500
+ function isAeroToolProfile(value) {
8501
+ return value !== void 0 && AERO_TOOL_PROFILES.includes(value);
8502
+ }
8503
+ var AeroAgentToolNames = Object.freeze({
8504
+ adsOperatorContext: "canonry_ads_operator_context"
8505
+ });
8506
+ var AERO_ADS_OPERATOR_CONTEXT_TOOL_NAME = AeroAgentToolNames.adsOperatorContext;
8507
+ var AERO_ADS_OPERATOR_MCP_TOOL_NAMES = /* @__PURE__ */ new Set([
8508
+ CanonryMcpToolNames.canonry_project_get,
8509
+ CanonryMcpToolNames.canonry_project_overview,
8510
+ CanonryMcpToolNames.canonry_search,
8511
+ CanonryMcpToolNames.canonry_doctor,
8512
+ CanonryMcpToolNames.canonry_analytics_metrics,
8513
+ CanonryMcpToolNames.canonry_analytics_sources,
8514
+ CanonryMcpToolNames.canonry_runs_list,
8515
+ CanonryMcpToolNames.canonry_runs_latest,
8516
+ CanonryMcpToolNames.canonry_run_get,
8517
+ CanonryMcpToolNames.canonry_insights_list,
8518
+ CanonryMcpToolNames.canonry_insight_get,
8519
+ CanonryMcpToolNames.canonry_health_latest,
8520
+ CanonryMcpToolNames.canonry_queries_list,
8521
+ CanonryMcpToolNames.canonry_competitors_list,
8522
+ CanonryMcpToolNames.canonry_schedule_get,
8523
+ CanonryMcpToolNames.canonry_memory_list,
8524
+ CanonryMcpToolNames.canonry_memory_set,
8525
+ CanonryMcpToolNames.canonry_memory_forget,
8526
+ CanonryMcpToolNames.canonry_run_trigger,
8527
+ CanonryMcpToolNames.canonry_ads_status,
8528
+ CanonryMcpToolNames.canonry_ads_campaigns,
8529
+ CanonryMcpToolNames.canonry_ads_insights,
8530
+ CanonryMcpToolNames.canonry_ads_summary,
8531
+ CanonryMcpToolNames.canonry_ads_sync
8532
+ ]);
8533
+ function toolResult(details) {
8534
+ return {
8535
+ content: [{ type: "text", text: truncateToolResult(details) }],
8536
+ details
8537
+ };
8538
+ }
8539
+ async function readSection(fn) {
8540
+ try {
8541
+ return { status: "ok", data: await fn() };
8542
+ } catch (err) {
8543
+ return {
8544
+ status: "error",
8545
+ error: err instanceof Error ? err.message : String(err)
8546
+ };
8547
+ }
8548
+ }
8549
+ function clampInteger(value, fallback, min, max) {
8550
+ if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
8551
+ return Math.max(min, Math.min(max, Math.trunc(value)));
8552
+ }
8553
+ function isoDateDaysAgo(days) {
8554
+ const d = new Date(Date.now() - (days - 1) * 24 * 60 * 60 * 1e3);
8555
+ return d.toISOString().slice(0, 10);
8556
+ }
8557
+ function todayIsoDate() {
8558
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
8559
+ }
8560
+ function compactOverview(overview) {
8561
+ return {
8562
+ project: overview.project,
8563
+ contextLabel: overview.contextLabel,
8564
+ dateRangeLabel: overview.dateRangeLabel,
8565
+ latestRun: overview.latestRun,
8566
+ health: overview.health,
8567
+ scores: overview.scores,
8568
+ queryCounts: overview.queryCounts,
8569
+ providers: overview.providers,
8570
+ citationMovement: overview.citationMovement,
8571
+ mentionMovement: overview.mentionMovement,
8572
+ movementComparison: overview.movementComparison,
8573
+ topInsights: overview.topInsights?.slice(0, 5) ?? [],
8574
+ attentionItems: overview.attentionItems?.slice(0, 8) ?? [],
8575
+ competitors: overview.competitors?.slice(0, 12) ?? []
8576
+ };
8577
+ }
8578
+ function compactCampaigns(response, limit) {
8579
+ return {
8580
+ campaignCount: response.campaigns.length,
8581
+ campaigns: response.campaigns.slice(0, limit).map((campaign) => ({
8582
+ id: campaign.id,
8583
+ name: campaign.name,
8584
+ status: campaign.status,
8585
+ biddingType: campaign.biddingType,
8586
+ dailySpendLimitMicros: campaign.dailySpendLimitMicros,
8587
+ lifetimeSpendLimitMicros: campaign.lifetimeSpendLimitMicros,
8588
+ adGroupCount: campaign.adGroups.length,
8589
+ adGroups: campaign.adGroups.slice(0, 8).map((group) => ({
8590
+ id: group.id,
8591
+ name: group.name,
8592
+ status: group.status,
8593
+ billingEventType: group.billingEventType,
8594
+ maxBidMicros: group.maxBidMicros,
8595
+ contextHintCount: group.contextHints.length,
8596
+ contextHints: group.contextHints.slice(0, 3),
8597
+ adCount: group.ads.length,
8598
+ ads: group.ads.slice(0, 4).map((ad) => ({
8599
+ id: ad.id,
8600
+ name: ad.name,
8601
+ status: ad.status,
8602
+ reviewStatus: ad.reviewStatus,
8603
+ creative: ad.creative ? {
8604
+ type: ad.creative.type,
8605
+ title: ad.creative.title,
8606
+ body: ad.creative.body,
8607
+ targetUrl: ad.creative.targetUrl
8608
+ } : ad.creative
8609
+ }))
8610
+ }))
8611
+ }))
8612
+ };
8613
+ }
8614
+ function compactInsights(response, rowLimit) {
8615
+ return {
8616
+ currencyCode: response.currencyCode,
8617
+ rowCount: response.rows.length,
8618
+ rows: response.rows.slice(-rowLimit)
8619
+ };
8620
+ }
8621
+ function buildAdsOperatorContextTool(ctx) {
8622
+ return {
8623
+ name: AERO_ADS_OPERATOR_CONTEXT_TOOL_NAME,
8624
+ label: "Get ads operator context",
8625
+ description: "One-call Aero context pack for ads operations. Reads the project overview, ads connection, paid summary, bounded campaign snapshots, recent paid rollups, ads doctor checks, and recent Aero memory. Use before diagnosing ChatGPT ads performance or planning the next operator action.",
8626
+ parameters: Type3.Object({
8627
+ windowDays: Type3.Optional(Type3.Integer({
8628
+ minimum: 1,
8629
+ maximum: 90,
8630
+ description: "Lookback window for paid rollup rows. Default 30 days."
8631
+ })),
8632
+ campaignLimit: Type3.Optional(Type3.Integer({
8633
+ minimum: 1,
8634
+ maximum: 25,
8635
+ description: "Max campaign snapshots to include. Default 8."
8636
+ })),
8637
+ insightRowLimit: Type3.Optional(Type3.Integer({
8638
+ minimum: 1,
8639
+ maximum: 100,
8640
+ description: "Max paid rollup rows to include per level. Default 40."
8641
+ }))
8642
+ }),
8643
+ execute: async (_toolCallId, rawParams) => {
8644
+ const params = rawParams;
8645
+ const windowDays = clampInteger(params.windowDays, 30, 1, 90);
8646
+ const campaignLimit = clampInteger(params.campaignLimit, 8, 1, 25);
8647
+ const insightRowLimit = clampInteger(params.insightRowLimit, 40, 1, 100);
8648
+ const from = isoDateDaysAgo(windowDays);
8649
+ const to = todayIsoDate();
8650
+ const [
8651
+ overview,
8652
+ adsStatus,
8653
+ adsSummary,
8654
+ campaigns,
8655
+ campaignInsights,
8656
+ adGroupInsights,
8657
+ doctor,
8658
+ memory
8659
+ ] = await Promise.all([
8660
+ readSection(() => ctx.client.getProjectOverview(ctx.projectName)),
8661
+ readSection(() => ctx.client.getAdsStatus(ctx.projectName)),
8662
+ readSection(() => ctx.client.getAdsSummary(ctx.projectName)),
8663
+ readSection(async () => compactCampaigns(await ctx.client.getAdsCampaigns(ctx.projectName), campaignLimit)),
8664
+ readSection(
8665
+ async () => compactInsights(
8666
+ await ctx.client.getAdsInsights(ctx.projectName, { level: "campaign", from, to }),
8667
+ insightRowLimit
8668
+ )
8669
+ ),
8670
+ readSection(
8671
+ async () => compactInsights(
8672
+ await ctx.client.getAdsInsights(ctx.projectName, { level: "ad_group", from, to }),
8673
+ insightRowLimit
8674
+ )
8675
+ ),
8676
+ readSection(() => ctx.client.runDoctor({ project: ctx.projectName, checkIds: ["ads.*"] })),
8677
+ readSection(async () => {
8678
+ const result = await ctx.client.listAgentMemory(ctx.projectName);
8679
+ return { entries: result.entries.slice(0, 10) };
8680
+ })
8681
+ ]);
8682
+ return toolResult({
8683
+ project: ctx.projectName,
8684
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
8685
+ window: { days: windowDays, from, to },
8686
+ overview: overview.status === "ok" && overview.data ? { status: "ok", data: compactOverview(overview.data) } : overview,
8687
+ ads: {
8688
+ status: adsStatus,
8689
+ summary: adsSummary,
8690
+ campaigns,
8691
+ insights: {
8692
+ campaign: campaignInsights,
8693
+ adGroup: adGroupInsights
8694
+ }
8695
+ },
8696
+ doctor,
8697
+ memory
8698
+ });
8699
+ }
8700
+ };
8701
+ }
8441
8702
  function buildReadTools(ctx) {
8442
8703
  return buildMcpAgentTools(canonryMcpTools, ctx, { readOnly: true });
8443
8704
  }
8444
8705
  function buildAllTools(ctx) {
8445
8706
  return buildMcpAgentTools(canonryMcpTools, ctx);
8446
8707
  }
8708
+ function buildAdsOperatorTools(ctx, opts = {}) {
8709
+ const mcpTools = buildMcpAgentTools(canonryMcpTools, ctx, {
8710
+ readOnly: opts.scope === AeroToolScopes.readOnly,
8711
+ includeNames: AERO_ADS_OPERATOR_MCP_TOOL_NAMES
8712
+ });
8713
+ return [buildAdsOperatorContextTool(ctx), ...mcpTools];
8714
+ }
8715
+ function buildAeroStateTools(ctx, opts = {}) {
8716
+ const scope = opts.scope ?? AeroToolScopes.all;
8717
+ const profile = opts.profile ?? AeroToolProfiles.default;
8718
+ if (profile === AeroToolProfiles.adsOperator) {
8719
+ return buildAdsOperatorTools(ctx, { scope });
8720
+ }
8721
+ return scope === AeroToolScopes.readOnly ? buildReadTools(ctx) : buildAllTools(ctx);
8722
+ }
8723
+
8724
+ // src/agent/llm-usage.ts
8725
+ import crypto18 from "crypto";
8726
+ var AeroLlmUsageFeatures = {
8727
+ turn: "aero.turn"
8728
+ };
8729
+ var AERO_PROMPT_FAMILY = "aero";
8730
+ var AERO_PROMPT_VERSION = "aero-system-v1";
8731
+ function dollarsToMillicents(dollars) {
8732
+ if (!Number.isFinite(dollars) || dollars <= 0) return 0;
8733
+ return Math.round(dollars * 1e5);
8734
+ }
8735
+ function nonNegativeInteger(value) {
8736
+ if (!Number.isFinite(value) || value === void 0 || value <= 0) return 0;
8737
+ return Math.round(value);
8738
+ }
8739
+ function recordLlmUsageEvent(args) {
8740
+ try {
8741
+ const usage = args.message.usage;
8742
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8743
+ args.db.insert(llmUsageEvents).values({
8744
+ id: crypto18.randomUUID(),
8745
+ projectId: args.projectId,
8746
+ runId: args.runId,
8747
+ agentSessionId: args.agentSessionId,
8748
+ feature: args.feature,
8749
+ provider: args.message.provider,
8750
+ model: args.message.model,
8751
+ responseId: args.message.responseId,
8752
+ inputTokens: nonNegativeInteger(usage.input),
8753
+ outputTokens: nonNegativeInteger(usage.output),
8754
+ cacheReadTokens: nonNegativeInteger(usage.cacheRead),
8755
+ cacheWriteTokens: nonNegativeInteger(usage.cacheWrite),
8756
+ totalTokens: nonNegativeInteger(usage.totalTokens),
8757
+ costMillicents: dollarsToMillicents(usage.cost.total),
8758
+ promptFamily: args.promptFamily,
8759
+ promptVersion: args.promptVersion,
8760
+ metadata: args.metadata,
8761
+ createdAt: now
8762
+ }).run();
8763
+ } catch {
8764
+ }
8765
+ }
8766
+
8767
+ // src/agent/prompt-cache.ts
8768
+ var AERO_MEMORY_SEPARATOR = "\n\n---\n\n";
8769
+ var AERO_MEMORY_BLOCK_PREFIX = `${AERO_MEMORY_SEPARATOR}<memory>
8770
+ `;
8771
+ function isRecord(value) {
8772
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
8773
+ }
8774
+ function isTextSystemBlock(value) {
8775
+ return isRecord(value) && value.type === "text" && typeof value.text === "string";
8776
+ }
8777
+ function splitAeroHydratedSystemPrompt(systemPrompt) {
8778
+ const idx = systemPrompt.lastIndexOf(AERO_MEMORY_BLOCK_PREFIX);
8779
+ if (idx < 0) return { basePrompt: systemPrompt };
8780
+ return {
8781
+ basePrompt: systemPrompt.slice(0, idx).trimEnd(),
8782
+ memoryBlock: systemPrompt.slice(idx + AERO_MEMORY_SEPARATOR.length)
8783
+ };
8784
+ }
8785
+ function splitAeroAnthropicSystemCachePayload(payload, model) {
8786
+ if (model.api !== "anthropic-messages") return void 0;
8787
+ if (!isRecord(payload) || !Array.isArray(payload.system)) return void 0;
8788
+ let changed = false;
8789
+ const nextSystem = [];
8790
+ for (const block of payload.system) {
8791
+ if (!isTextSystemBlock(block)) {
8792
+ nextSystem.push(block);
8793
+ continue;
8794
+ }
8795
+ const split = splitAeroHydratedSystemPrompt(block.text);
8796
+ if (!split.memoryBlock) {
8797
+ nextSystem.push(block);
8798
+ continue;
8799
+ }
8800
+ const memoryBlock = {
8801
+ ...block,
8802
+ text: split.memoryBlock
8803
+ };
8804
+ delete memoryBlock.cache_control;
8805
+ nextSystem.push({
8806
+ ...block,
8807
+ text: split.basePrompt
8808
+ });
8809
+ nextSystem.push(memoryBlock);
8810
+ changed = true;
8811
+ }
8812
+ if (!changed) return void 0;
8813
+ return {
8814
+ ...payload,
8815
+ system: nextSystem
8816
+ };
8817
+ }
8818
+
8819
+ // src/agent/tool-usage.ts
8820
+ import crypto19 from "crypto";
8821
+ var AeroToolEventStatuses = {
8822
+ success: "success",
8823
+ error: "error"
8824
+ };
8825
+ function jsonBytes(value) {
8826
+ try {
8827
+ return Buffer.byteLength(JSON.stringify(value), "utf8");
8828
+ } catch {
8829
+ return 0;
8830
+ }
8831
+ }
8832
+ function nonNegativeInteger2(value) {
8833
+ if (!Number.isFinite(value) || value === void 0 || value <= 0) return 0;
8834
+ return Math.round(value);
8835
+ }
8836
+ function resultTextChars(content) {
8837
+ let total = 0;
8838
+ for (const block of content) {
8839
+ if (block.type === "text") {
8840
+ total += block.text.length;
8841
+ } else {
8842
+ total += block.data.length;
8843
+ }
8844
+ }
8845
+ return total;
8846
+ }
8847
+ function createAeroToolUsageHooks(args) {
8848
+ const startedAt = /* @__PURE__ */ new Map();
8849
+ return {
8850
+ beforeToolCall: async ({ toolCall }) => {
8851
+ startedAt.set(toolCall.id, Date.now());
8852
+ return void 0;
8853
+ },
8854
+ afterToolCall: async ({ assistantMessage, toolCall, args: toolArgs, result, isError, context }) => {
8855
+ const start = startedAt.get(toolCall.id);
8856
+ startedAt.delete(toolCall.id);
8857
+ recordAgentToolEvent({
8858
+ db: args.db,
8859
+ projectId: args.projectId,
8860
+ agentSessionId: args.agentSessionId,
8861
+ toolCallId: toolCall.id,
8862
+ toolName: toolCall.name,
8863
+ assistantResponseId: assistantMessage.responseId,
8864
+ provider: assistantMessage.provider,
8865
+ model: assistantMessage.model,
8866
+ status: isError ? AeroToolEventStatuses.error : AeroToolEventStatuses.success,
8867
+ durationMs: start === void 0 ? 0 : Date.now() - start,
8868
+ argsBytes: jsonBytes(toolArgs),
8869
+ resultTextChars: resultTextChars(result.content),
8870
+ resultBytes: jsonBytes(result),
8871
+ metadata: {
8872
+ ...args.metadata,
8873
+ toolCount: context.tools?.length ?? 0
8874
+ }
8875
+ });
8876
+ return void 0;
8877
+ }
8878
+ };
8879
+ }
8880
+ function recordAgentToolEvent(args) {
8881
+ try {
8882
+ args.db.insert(agentToolEvents).values({
8883
+ id: crypto19.randomUUID(),
8884
+ projectId: args.projectId,
8885
+ agentSessionId: args.agentSessionId,
8886
+ toolCallId: args.toolCallId,
8887
+ toolName: args.toolName,
8888
+ assistantResponseId: args.assistantResponseId,
8889
+ provider: args.provider,
8890
+ model: args.model,
8891
+ status: args.status,
8892
+ durationMs: nonNegativeInteger2(args.durationMs),
8893
+ argsBytes: nonNegativeInteger2(args.argsBytes),
8894
+ resultTextChars: nonNegativeInteger2(args.resultTextChars),
8895
+ resultBytes: nonNegativeInteger2(args.resultBytes),
8896
+ metadata: args.metadata,
8897
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
8898
+ }).run();
8899
+ } catch {
8900
+ }
8901
+ }
8447
8902
 
8448
8903
  // src/agent/session.ts
8449
8904
  var builtinsRegistered = false;
@@ -8502,20 +8957,30 @@ function resolveAeroModel(provider, modelId) {
8502
8957
  function buildApiKeyResolver(config) {
8503
8958
  return (piAiProvider) => resolveApiKeyFor(piAiProvider, config);
8504
8959
  }
8960
+ function buildAeroProviderSessionId(opts) {
8961
+ return `canonry:aero:${opts.agentSessionId ?? opts.projectId ?? opts.projectName}`;
8962
+ }
8505
8963
  function createAeroSession(opts) {
8506
8964
  const systemPrompt = opts.systemPromptOverride ?? loadAeroSystemPrompt();
8507
8965
  const provider = opts.provider ?? detectAgentProvider(opts.config);
8508
8966
  if (!provider) throw new Error(missingProviderMessage());
8509
8967
  const model = resolveAeroModel(provider, opts.modelId);
8510
- const toolScope = opts.toolScope ?? "all";
8968
+ const toolScope = opts.toolScope ?? AeroToolScopes.all;
8969
+ const toolProfile = opts.toolProfile ?? AeroToolProfiles.default;
8511
8970
  const toolCtx = {
8512
8971
  client: opts.client,
8513
8972
  projectName: opts.projectName
8514
8973
  };
8515
- const stateTools = toolScope === "read-only" ? buildReadTools(toolCtx) : buildAllTools(toolCtx);
8974
+ const stateTools = buildAeroStateTools(toolCtx, { scope: toolScope, profile: toolProfile });
8516
8975
  const defaultTools = [...stateTools, ...buildSkillDocTools()];
8517
8976
  const tools = opts.tools ?? defaultTools;
8518
- return new Agent({
8977
+ const toolUsageHooks = opts.db ? createAeroToolUsageHooks({
8978
+ db: opts.db,
8979
+ projectId: opts.projectId,
8980
+ agentSessionId: opts.agentSessionId,
8981
+ metadata: { projectName: opts.projectName }
8982
+ }) : {};
8983
+ const agent = new Agent({
8519
8984
  initialState: {
8520
8985
  systemPrompt,
8521
8986
  model,
@@ -8523,8 +8988,29 @@ function createAeroSession(opts) {
8523
8988
  ...opts.initialMessages ? { messages: opts.initialMessages } : {}
8524
8989
  },
8525
8990
  streamFn: opts.streamFn,
8991
+ sessionId: buildAeroProviderSessionId(opts),
8992
+ onPayload: splitAeroAnthropicSystemCachePayload,
8993
+ ...toolUsageHooks,
8526
8994
  getApiKey: buildApiKeyResolver(opts.config)
8527
8995
  });
8996
+ const telemetryDb = opts.db;
8997
+ if (telemetryDb) {
8998
+ agent.subscribe((event) => {
8999
+ if (event.type !== "turn_end") return;
9000
+ if (event.message.role !== "assistant") return;
9001
+ recordLlmUsageEvent({
9002
+ db: telemetryDb,
9003
+ projectId: opts.projectId,
9004
+ agentSessionId: opts.agentSessionId,
9005
+ feature: AeroLlmUsageFeatures.turn,
9006
+ promptFamily: AERO_PROMPT_FAMILY,
9007
+ promptVersion: AERO_PROMPT_VERSION,
9008
+ message: event.message,
9009
+ metadata: { projectName: opts.projectName, toolCount: agent.state.tools.length }
9010
+ });
9011
+ });
9012
+ }
9013
+ return agent;
8528
9014
  }
8529
9015
  function resolveSessionProviderAndModel(config, opts) {
8530
9016
  const provider = opts?.provider ?? detectAgentProvider(config);
@@ -8534,7 +9020,7 @@ function resolveSessionProviderAndModel(config, opts) {
8534
9020
  }
8535
9021
 
8536
9022
  // src/agent/remote-mcp.ts
8537
- import { Type as Type3 } from "@sinclair/typebox";
9023
+ import { Type as Type4 } from "@sinclair/typebox";
8538
9024
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
8539
9025
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
8540
9026
  var log18 = createLogger("RemoteMcp");
@@ -8561,7 +9047,7 @@ function isReadOnly(tool) {
8561
9047
  return tool.annotations?.readOnlyHint === true;
8562
9048
  }
8563
9049
  function adaptRemoteTool(client, tool) {
8564
- const parameters = Type3.Unsafe(
9050
+ const parameters = Type4.Unsafe(
8565
9051
  tool.inputSchema ?? { type: "object", properties: {} }
8566
9052
  );
8567
9053
  const execute = async (_toolCallId, params) => {
@@ -8621,7 +9107,7 @@ async function loadExternalMcpTools(servers, opts = {}) {
8621
9107
  }
8622
9108
 
8623
9109
  // src/agent/memory-store.ts
8624
- import crypto18 from "crypto";
9110
+ import crypto20 from "crypto";
8625
9111
  import { and as and13, desc as desc6, eq as eq17, like, sql as sql5 } from "drizzle-orm";
8626
9112
  var COMPACTION_KEY_PREFIX = "compaction:";
8627
9113
  var COMPACTION_NOTES_PER_SESSION = 3;
@@ -8650,7 +9136,7 @@ function upsertMemoryEntry(db, args) {
8650
9136
  throw new Error(`memory key prefix "${COMPACTION_KEY_PREFIX}" is reserved for compaction notes`);
8651
9137
  }
8652
9138
  const now = (/* @__PURE__ */ new Date()).toISOString();
8653
- const id = crypto18.randomUUID();
9139
+ const id = crypto20.randomUUID();
8654
9140
  db.insert(agentMemory).values({
8655
9141
  id,
8656
9142
  projectId: args.projectId,
@@ -8687,7 +9173,7 @@ function writeCompactionNote(db, args) {
8687
9173
  }
8688
9174
  const now = (/* @__PURE__ */ new Date()).toISOString();
8689
9175
  const key = `${COMPACTION_KEY_PREFIX}${args.sessionId}:${now}`;
8690
- const id = crypto18.randomUUID();
9176
+ const id = crypto20.randomUUID();
8691
9177
  let inserted;
8692
9178
  db.transaction((tx) => {
8693
9179
  tx.insert(agentMemory).values({
@@ -8854,6 +9340,8 @@ var SessionRegistry = class {
8854
9340
  pending = /* @__PURE__ */ new Map();
8855
9341
  /** Last tool scope used on the live Agent for a project. Read in getOrCreate to know when to swap. */
8856
9342
  scopes = /* @__PURE__ */ new Map();
9343
+ /** Last profile used on the live Agent for a project. Paired with scopes when rebuilding tools. */
9344
+ profiles = /* @__PURE__ */ new Map();
8857
9345
  /** Cached resolved project id per project name, used so alignScope can rebuild tool context without a DB roundtrip. */
8858
9346
  projectIds = /* @__PURE__ */ new Map();
8859
9347
  /**
@@ -8945,9 +9433,14 @@ var SessionRegistry = class {
8945
9433
  modelId: effectiveModelId,
8946
9434
  systemPromptOverride: this.buildHydratedSystemPrompt(projectId, row.systemPrompt),
8947
9435
  initialMessages: persistedMessages,
8948
- toolScope: preferences?.toolScope
9436
+ toolScope: preferences?.toolScope,
9437
+ toolProfile: preferences?.toolProfile,
9438
+ db: this.opts.db,
9439
+ projectId,
9440
+ agentSessionId: row.id
8949
9441
  });
8950
- this.scopes.set(projectName, preferences?.toolScope ?? "all");
9442
+ this.scopes.set(projectName, preferences?.toolScope ?? AeroToolScopes.all);
9443
+ this.profiles.set(projectName, preferences?.toolProfile ?? AeroToolProfiles.default);
8951
9444
  this.projectIds.set(projectName, projectId);
8952
9445
  if (queued.length > 0) {
8953
9446
  this.appendPending(projectName, queued);
@@ -8959,6 +9452,7 @@ var SessionRegistry = class {
8959
9452
  }
8960
9453
  const { provider, modelId } = resolveSessionProviderAndModel(this.opts.config, preferences);
8961
9454
  const systemPrompt = loadAeroSystemPrompt();
9455
+ const sessionId = crypto21.randomUUID();
8962
9456
  const agent = createAeroSession({
8963
9457
  projectName,
8964
9458
  client: this.opts.client,
@@ -8968,11 +9462,17 @@ var SessionRegistry = class {
8968
9462
  // Hydrate on the fresh path too — a brand-new session may still see
8969
9463
  // notes if they were seeded via CLI/API before the first prompt.
8970
9464
  systemPromptOverride: this.buildHydratedSystemPrompt(projectId, systemPrompt),
8971
- toolScope: preferences?.toolScope
9465
+ toolScope: preferences?.toolScope,
9466
+ toolProfile: preferences?.toolProfile,
9467
+ db: this.opts.db,
9468
+ projectId,
9469
+ agentSessionId: sessionId
8972
9470
  });
8973
- this.scopes.set(projectName, preferences?.toolScope ?? "all");
9471
+ this.scopes.set(projectName, preferences?.toolScope ?? AeroToolScopes.all);
9472
+ this.profiles.set(projectName, preferences?.toolProfile ?? AeroToolProfiles.default);
8974
9473
  this.projectIds.set(projectName, projectId);
8975
9474
  this.insertRow({
9475
+ id: sessionId,
8976
9476
  projectId,
8977
9477
  // Persist the raw (unhydrated) prompt so the DB remains canonical —
8978
9478
  // the `<memory>` block is rebuilt from the notes table on every load.
@@ -9049,8 +9549,9 @@ ${lines.join("\n")}
9049
9549
  * Busy-check runs FIRST, before any state mutation — if two requests race
9050
9550
  * on the same project, one gets the 409 and the other's in-flight turn is
9051
9551
  * untouched. Only after confirming idle do we:
9052
- * - align `state.tools` to the requested scope (CLI full vs dashboard
9053
- * read-only share the same cached Agent; each request re-scopes it).
9552
+ * - align `state.tools` to the requested scope/profile (CLI full vs
9553
+ * dashboard read-only share the same cached Agent; each request
9554
+ * re-scopes it).
9054
9555
  * - align `state.model` when the caller passes `provider` or `modelId`,
9055
9556
  * honoring `--provider` / `--model` on hot sessions (not just on
9056
9557
  * fresh/hydrated construction).
@@ -9063,7 +9564,10 @@ ${lines.join("\n")}
9063
9564
  if (agent.state.isStreaming) {
9064
9565
  throw agentBusy(projectName);
9065
9566
  }
9066
- this.alignScope(projectName, agent, preferences?.toolScope ?? "all");
9567
+ this.alignToolSurface(projectName, agent, {
9568
+ scope: preferences?.toolScope ?? AeroToolScopes.all,
9569
+ profile: preferences?.toolProfile ?? AeroToolProfiles.default
9570
+ });
9067
9571
  if (preferences?.provider || preferences?.modelId) {
9068
9572
  this.alignModel(projectName, agent, preferences);
9069
9573
  }
@@ -9126,14 +9630,17 @@ ${lines.join("\n")}
9126
9630
  });
9127
9631
  }
9128
9632
  }
9129
- alignScope(projectName, agent, wantScope) {
9130
- if (this.scopes.get(projectName) === wantScope) return;
9633
+ alignToolSurface(projectName, agent, want) {
9634
+ if (this.scopes.get(projectName) === want.scope && this.profiles.get(projectName) === want.profile) {
9635
+ return;
9636
+ }
9131
9637
  const projectId = this.projectIds.get(projectName) ?? this.resolveProjectId(projectName);
9132
9638
  this.projectIds.set(projectName, projectId);
9133
9639
  const toolCtx = { client: this.opts.client, projectName };
9134
- const stateTools = wantScope === "read-only" ? buildReadTools(toolCtx) : buildAllTools(toolCtx);
9640
+ const stateTools = buildAeroStateTools(toolCtx, want);
9135
9641
  agent.state.tools = [...stateTools, ...buildSkillDocTools()];
9136
- this.scopes.set(projectName, wantScope);
9642
+ this.scopes.set(projectName, want.scope);
9643
+ this.profiles.set(projectName, want.profile);
9137
9644
  }
9138
9645
  alignModel(projectName, agent, preferences) {
9139
9646
  const projectId = this.tryResolveProjectId(projectName);
@@ -9206,8 +9713,9 @@ ${lines.join("\n")}
9206
9713
  try {
9207
9714
  let agent;
9208
9715
  try {
9209
- const scope = this.scopes.get(projectName) ?? "read-only";
9210
- agent = await this.acquireForTurn(projectName, { toolScope: scope });
9716
+ const scope = this.scopes.get(projectName) ?? AeroToolScopes.readOnly;
9717
+ const profile = this.profiles.get(projectName) ?? AeroToolProfiles.default;
9718
+ agent = await this.acquireForTurn(projectName, { toolScope: scope, toolProfile: profile });
9211
9719
  } catch (err) {
9212
9720
  if (err.code === "AGENT_BUSY") return;
9213
9721
  throw err;
@@ -9242,6 +9750,7 @@ ${lines.join("\n")}
9242
9750
  this.live.delete(projectName);
9243
9751
  this.pending.delete(projectName);
9244
9752
  this.scopes.delete(projectName);
9753
+ this.profiles.delete(projectName);
9245
9754
  this.projectIds.delete(projectName);
9246
9755
  }
9247
9756
  /** Evict every live Agent. Durable state in DB is untouched. */
@@ -9322,7 +9831,7 @@ ${lines.join("\n")}
9322
9831
  insertRow(params) {
9323
9832
  const now = (/* @__PURE__ */ new Date()).toISOString();
9324
9833
  this.opts.db.insert(agentSessions).values({
9325
- id: crypto19.randomUUID(),
9834
+ id: params.id ?? crypto21.randomUUID(),
9326
9835
  projectId: params.projectId,
9327
9836
  systemPrompt: params.systemPrompt,
9328
9837
  modelProvider: params.provider ?? params.modelProvider ?? AgentProviderIds.claude,
@@ -9381,13 +9890,16 @@ function registerAgentRoutes(app, opts) {
9381
9890
  );
9382
9891
  app.post("/projects/:name/agent/prompt", async (request, reply) => {
9383
9892
  const project = resolveProject(opts.db, request.params.name);
9384
- const promptText = (request.body?.prompt ?? "").trim();
9893
+ const body = request.body;
9894
+ const promptText = (body?.prompt ?? "").trim();
9385
9895
  if (!promptText) throw validationError('"prompt" is required');
9386
- const requestedScope = request.body?.scope === "all" ? "all" : "read-only";
9896
+ const requestedScope = body?.scope === AeroToolScopes.all ? AeroToolScopes.all : AeroToolScopes.readOnly;
9897
+ const requestedProfile = isAeroToolProfile(body?.profile) ? body.profile : AeroToolProfiles.default;
9387
9898
  const agent = await opts.sessionRegistry.acquireForTurn(project.name, {
9388
- provider: request.body?.provider,
9389
- modelId: request.body?.modelId,
9390
- toolScope: requestedScope
9899
+ provider: body?.provider,
9900
+ modelId: body?.modelId,
9901
+ toolScope: requestedScope,
9902
+ toolProfile: requestedProfile
9391
9903
  });
9392
9904
  reply.raw.writeHead(200, {
9393
9905
  "Content-Type": "text/event-stream",
@@ -9500,7 +10012,7 @@ Output requirements:
9500
10012
  - Final bullet: the expected outcome if executed (recovered citations, ranking lift, etc.).
9501
10013
  - No preamble, no closing pleasantries, no markdown headers.
9502
10014
  - Maximum 600 characters total. Be dense.`;
9503
- function dollarsToMillicents(dollars) {
10015
+ function dollarsToMillicents2(dollars) {
9504
10016
  if (!Number.isFinite(dollars) || dollars <= 0) return 0;
9505
10017
  return Math.round(dollars * 1e5);
9506
10018
  }
@@ -9607,7 +10119,7 @@ function createRecommendationExplainer(opts) {
9607
10119
  provider,
9608
10120
  model: model.id,
9609
10121
  responseText: text,
9610
- costMillicents: dollarsToMillicents(resp.usage.cost.total)
10122
+ costMillicents: dollarsToMillicents2(resp.usage.cost.total)
9611
10123
  };
9612
10124
  };
9613
10125
  }
@@ -9685,7 +10197,7 @@ Your previous reply was not valid JSON. Return ONLY a JSON object with exactly t
9685
10197
  provider,
9686
10198
  model: model.id,
9687
10199
  brief,
9688
- costMillicents: dollarsToMillicents(totalCostDollars)
10200
+ costMillicents: dollarsToMillicents2(totalCostDollars)
9689
10201
  };
9690
10202
  }
9691
10203
  }
@@ -10447,14 +10959,14 @@ function summarizeProviderConfig(config) {
10447
10959
  };
10448
10960
  }
10449
10961
  function hashApiKey(key) {
10450
- return crypto20.createHash("sha256").update(key).digest("hex");
10962
+ return crypto22.createHash("sha256").update(key).digest("hex");
10451
10963
  }
10452
10964
  var DASHBOARD_SCRYPT_KEYLEN = 64;
10453
10965
  var DASHBOARD_SCRYPT_COST = 1 << 15;
10454
10966
  var DASHBOARD_SCRYPT_MAXMEM = 64 * 1024 * 1024;
10455
10967
  function hashDashboardPassword(password) {
10456
- const salt = crypto20.randomBytes(16);
10457
- const derived = crypto20.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
10968
+ const salt = crypto22.randomBytes(16);
10969
+ const derived = crypto22.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
10458
10970
  N: DASHBOARD_SCRYPT_COST,
10459
10971
  maxmem: DASHBOARD_SCRYPT_MAXMEM
10460
10972
  });
@@ -10475,14 +10987,14 @@ function verifyDashboardPassword(password, storedHash) {
10475
10987
  } catch {
10476
10988
  return { ok: false, needsRehash: false };
10477
10989
  }
10478
- const derived = crypto20.scryptSync(password, salt, expected.length, {
10990
+ const derived = crypto22.scryptSync(password, salt, expected.length, {
10479
10991
  N: DASHBOARD_SCRYPT_COST,
10480
10992
  maxmem: DASHBOARD_SCRYPT_MAXMEM
10481
10993
  });
10482
10994
  if (derived.length !== expected.length)
10483
10995
  return { ok: false, needsRehash: false };
10484
10996
  return {
10485
- ok: crypto20.timingSafeEqual(derived, expected),
10997
+ ok: crypto22.timingSafeEqual(derived, expected),
10486
10998
  needsRehash: false
10487
10999
  };
10488
11000
  }
@@ -10491,7 +11003,7 @@ function verifyDashboardPassword(password, storedHash) {
10491
11003
  const expected = Buffer.from(storedHash, "hex");
10492
11004
  if (candidate.length !== expected.length)
10493
11005
  return { ok: false, needsRehash: false };
10494
- const ok = crypto20.timingSafeEqual(candidate, expected);
11006
+ const ok = crypto22.timingSafeEqual(candidate, expected);
10495
11007
  return { ok, needsRehash: ok };
10496
11008
  }
10497
11009
  return { ok: false, needsRehash: false };
@@ -10961,7 +11473,7 @@ async function createServer(opts) {
10961
11473
  return removed;
10962
11474
  }
10963
11475
  };
10964
- const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto20.randomBytes(32).toString("hex");
11476
+ const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto22.randomBytes(32).toString("hex");
10965
11477
  const googleConnectionStore = {
10966
11478
  listConnections: (domain) => listGoogleConnections(opts.config, domain),
10967
11479
  getConnection: (domain, connectionType) => getGoogleConnection(opts.config, domain, connectionType),
@@ -11022,7 +11534,7 @@ async function createServer(opts) {
11022
11534
  if (!existing) {
11023
11535
  const prefix = opts.config.apiKey.slice(0, 12);
11024
11536
  opts.db.insert(apiKeys).values({
11025
- id: `key_${crypto20.randomBytes(8).toString("hex")}`,
11537
+ id: `key_${crypto22.randomBytes(8).toString("hex")}`,
11026
11538
  name: "default",
11027
11539
  keyHash,
11028
11540
  keyPrefix: prefix,
@@ -11046,7 +11558,7 @@ async function createServer(opts) {
11046
11558
  };
11047
11559
  const createSession = (apiKeyId) => {
11048
11560
  pruneExpiredSessions();
11049
- const sessionId = crypto20.randomBytes(32).toString("hex");
11561
+ const sessionId = crypto22.randomBytes(32).toString("hex");
11050
11562
  sessions.set(sessionId, {
11051
11563
  apiKeyId,
11052
11564
  expiresAt: Date.now() + SESSION_TTL_MS
@@ -11346,7 +11858,7 @@ async function createServer(opts) {
11346
11858
  deps: {
11347
11859
  enqueueAutoExtract: ({ projectId, release: r }) => {
11348
11860
  const now = (/* @__PURE__ */ new Date()).toISOString();
11349
- const runId = crypto20.randomUUID();
11861
+ const runId = crypto22.randomUUID();
11350
11862
  opts.db.insert(runs).values({
11351
11863
  id: runId,
11352
11864
  projectId,
@@ -11564,7 +12076,7 @@ async function createServer(opts) {
11564
12076
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
11565
12077
  opts.db.insert(auditLog).values(
11566
12078
  targetProjectIds.map((projectId) => ({
11567
- id: crypto20.randomUUID(),
12079
+ id: crypto22.randomUUID(),
11568
12080
  projectId,
11569
12081
  actor: "api",
11570
12082
  action: existing ? "provider.updated" : "provider.created",
@@ -11896,5 +12408,7 @@ export {
11896
12408
  checkLatestVersionForCli,
11897
12409
  listAgentProviders,
11898
12410
  coerceAgentProvider,
12411
+ AERO_TOOL_PROFILES,
12412
+ isAeroToolProfile,
11899
12413
  createServer
11900
12414
  };