@ainyc/canonry 4.100.0 → 4.101.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 (27) hide show
  1. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +0 -12
  2. package/assets/assets/{BacklinksPage-DWZdWEm3.js → BacklinksPage-aysk_iGy.js} +1 -1
  3. package/assets/assets/{ChartPrimitives-C-ky5206.js → ChartPrimitives-CmKqxndA.js} +1 -1
  4. package/assets/assets/ProjectPage-dduZrrBr.js +7 -0
  5. package/assets/assets/{RunRow-Bj-D4kZe.js → RunRow-Ba77hJ4d.js} +1 -1
  6. package/assets/assets/{RunsPage-GktitsAV.js → RunsPage-D3IAdQMB.js} +1 -1
  7. package/assets/assets/{SettingsPage-AsFLFDS3.js → SettingsPage-Y54JtLw-.js} +1 -1
  8. package/assets/assets/{TrafficPage-Cf3uCkxs.js → TrafficPage-DawzPWdt.js} +1 -1
  9. package/assets/assets/{TrafficSourceDetailPage-B5t7nf9C.js → TrafficSourceDetailPage-DAhQwFVb.js} +1 -1
  10. package/assets/assets/{arrow-left-CZFQuQO_.js → arrow-left-NsJbgqmV.js} +1 -1
  11. package/assets/assets/{extract-error-message--bieZ2IX.js → extract-error-message-u00ggxeK.js} +1 -1
  12. package/assets/assets/index-4YArcfwj.js +210 -0
  13. package/assets/assets/index-BCuWbxUj.css +1 -0
  14. package/assets/assets/{trash-2-sKO0k75f.js → trash-2-BBdbbe60.js} +1 -1
  15. package/assets/index.html +2 -2
  16. package/dist/{chunk-UVIHWF45.js → chunk-2YPVK75M.js} +6 -28
  17. package/dist/{chunk-SNHYRSUF.js → chunk-AV3DHH6B.js} +153 -369
  18. package/dist/{chunk-ADLS64PG.js → chunk-HUL5FUBD.js} +6 -105
  19. package/dist/{chunk-74SOIAIY.js → chunk-O3SFVGDF.js} +531 -49
  20. package/dist/cli.js +26 -114
  21. package/dist/index.js +4 -4
  22. package/dist/{intelligence-service-3CRNJLUZ.js → intelligence-service-R6QJ3KC2.js} +2 -2
  23. package/dist/mcp.js +2 -2
  24. package/package.json +7 -7
  25. package/assets/assets/ProjectPage-CpbFpi6J.js +0 -6
  26. package/assets/assets/index-9hI0ODQU.css +0 -1
  27. package/assets/assets/index-Cb2h7KkD.js +0 -210
@@ -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-UVIHWF45.js";
13
+ } from "./chunk-2YPVK75M.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,
@@ -88,6 +90,7 @@ import {
88
90
  listLocations,
89
91
  listMonthlyKeywords,
90
92
  listPlaceActionLinks,
93
+ llmUsageEvents,
91
94
  loadDuckdb,
92
95
  markSessionFailed,
93
96
  migrate,
@@ -111,7 +114,7 @@ import {
111
114
  siteAuditPages,
112
115
  siteAuditSnapshots,
113
116
  usageCounters
114
- } from "./chunk-SNHYRSUF.js";
117
+ } from "./chunk-AV3DHH6B.js";
115
118
  import {
116
119
  AGENT_MEMORY_VALUE_MAX_BYTES,
117
120
  AGENT_PROVIDER_IDS,
@@ -171,7 +174,7 @@ import {
171
174
  validationError,
172
175
  winnabilityClassLabel,
173
176
  withRetry
174
- } from "./chunk-ADLS64PG.js";
177
+ } from "./chunk-HUL5FUBD.js";
175
178
 
176
179
  // src/telemetry.ts
177
180
  import crypto from "crypto";
@@ -459,7 +462,7 @@ function checkLatestVersionForServer(opts) {
459
462
 
460
463
  // src/server.ts
461
464
  import { createRequire as createRequire3 } from "module";
462
- import crypto20 from "crypto";
465
+ import crypto22 from "crypto";
463
466
  import fs8 from "fs";
464
467
  import path9 from "path";
465
468
  import { fileURLToPath as fileURLToPath3 } from "url";
@@ -6403,7 +6406,7 @@ function readStoredGroundingSources(rawResponse) {
6403
6406
  return result;
6404
6407
  }
6405
6408
  async function backfillInsightsCommand(project, opts) {
6406
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-3CRNJLUZ.js");
6409
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-R6QJ3KC2.js");
6407
6410
  const config = loadConfig();
6408
6411
  const db = createClient(config.database);
6409
6412
  migrate(db);
@@ -7979,7 +7982,7 @@ function analysisResultFromInsights(insights2) {
7979
7982
  }
7980
7983
 
7981
7984
  // src/agent/session-registry.ts
7982
- import crypto19 from "crypto";
7985
+ import crypto21 from "crypto";
7983
7986
  import { eq as eq18 } from "drizzle-orm";
7984
7987
 
7985
7988
  // src/agent/session.ts
@@ -8140,6 +8143,11 @@ function resolveModelForCapability(provider, capability, modelIdOverride) {
8140
8143
  `Model '${id}' not found for pi-ai provider '${entry.piAiProvider}'. Verify PROVIDER_MODELS[${provider}][${capability}] against the installed @mariozechner/pi-ai catalog.`
8141
8144
  );
8142
8145
  }
8146
+ if (provider === AgentProviderIds.gemini && process.env.GEMINI_BASE_URL) {
8147
+ const base = process.env.GEMINI_BASE_URL.replace(/\/+$/, "");
8148
+ const baseUrl = base.endsWith("/v1beta") ? base : `${base}/v1beta`;
8149
+ return { ...model, baseUrl };
8150
+ }
8143
8151
  return model;
8144
8152
  }
8145
8153
  function buildOpenAiCompatibleModel(entry, id, suppressThinking = false) {
@@ -8332,6 +8340,9 @@ function buildSkillDocTools() {
8332
8340
  ];
8333
8341
  }
8334
8342
 
8343
+ // src/agent/tools.ts
8344
+ import { Type as Type3 } from "@sinclair/typebox";
8345
+
8335
8346
  // src/agent/mcp-to-agent-tool.ts
8336
8347
  import { Type as Type2 } from "@sinclair/typebox";
8337
8348
  var MAX_TOOL_RESULT_CHARS = 2e4;
@@ -8431,19 +8442,431 @@ function mcpToAgentTool(tool, ctx) {
8431
8442
  };
8432
8443
  }
8433
8444
  var AERO_EXCLUDED_MCP_TOOLS = /* @__PURE__ */ new Set([
8434
- "canonry_agent_clear"
8445
+ CanonryMcpToolNames.canonry_agent_clear
8435
8446
  ]);
8436
8447
  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));
8448
+ 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
8449
  }
8439
8450
 
8440
8451
  // src/agent/tools.ts
8452
+ var AeroToolScopes = Object.freeze({
8453
+ all: "all",
8454
+ readOnly: "read-only"
8455
+ });
8456
+ var AERO_TOOL_SCOPES = [
8457
+ AeroToolScopes.all,
8458
+ AeroToolScopes.readOnly
8459
+ ];
8460
+ var AeroToolProfiles = Object.freeze({
8461
+ default: "default",
8462
+ adsOperator: "ads-operator"
8463
+ });
8464
+ var AERO_TOOL_PROFILES = [
8465
+ AeroToolProfiles.default,
8466
+ AeroToolProfiles.adsOperator
8467
+ ];
8468
+ function isAeroToolProfile(value) {
8469
+ return value !== void 0 && AERO_TOOL_PROFILES.includes(value);
8470
+ }
8471
+ var AeroAgentToolNames = Object.freeze({
8472
+ adsOperatorContext: "canonry_ads_operator_context"
8473
+ });
8474
+ var AERO_ADS_OPERATOR_CONTEXT_TOOL_NAME = AeroAgentToolNames.adsOperatorContext;
8475
+ var AERO_ADS_OPERATOR_MCP_TOOL_NAMES = /* @__PURE__ */ new Set([
8476
+ CanonryMcpToolNames.canonry_project_get,
8477
+ CanonryMcpToolNames.canonry_project_overview,
8478
+ CanonryMcpToolNames.canonry_search,
8479
+ CanonryMcpToolNames.canonry_doctor,
8480
+ CanonryMcpToolNames.canonry_analytics_metrics,
8481
+ CanonryMcpToolNames.canonry_analytics_sources,
8482
+ CanonryMcpToolNames.canonry_runs_list,
8483
+ CanonryMcpToolNames.canonry_runs_latest,
8484
+ CanonryMcpToolNames.canonry_run_get,
8485
+ CanonryMcpToolNames.canonry_insights_list,
8486
+ CanonryMcpToolNames.canonry_insight_get,
8487
+ CanonryMcpToolNames.canonry_health_latest,
8488
+ CanonryMcpToolNames.canonry_queries_list,
8489
+ CanonryMcpToolNames.canonry_competitors_list,
8490
+ CanonryMcpToolNames.canonry_schedule_get,
8491
+ CanonryMcpToolNames.canonry_memory_list,
8492
+ CanonryMcpToolNames.canonry_memory_set,
8493
+ CanonryMcpToolNames.canonry_memory_forget,
8494
+ CanonryMcpToolNames.canonry_run_trigger,
8495
+ CanonryMcpToolNames.canonry_ads_status,
8496
+ CanonryMcpToolNames.canonry_ads_campaigns,
8497
+ CanonryMcpToolNames.canonry_ads_insights,
8498
+ CanonryMcpToolNames.canonry_ads_summary,
8499
+ CanonryMcpToolNames.canonry_ads_sync
8500
+ ]);
8501
+ function toolResult(details) {
8502
+ return {
8503
+ content: [{ type: "text", text: truncateToolResult(details) }],
8504
+ details
8505
+ };
8506
+ }
8507
+ async function readSection(fn) {
8508
+ try {
8509
+ return { status: "ok", data: await fn() };
8510
+ } catch (err) {
8511
+ return {
8512
+ status: "error",
8513
+ error: err instanceof Error ? err.message : String(err)
8514
+ };
8515
+ }
8516
+ }
8517
+ function clampInteger(value, fallback, min, max) {
8518
+ if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
8519
+ return Math.max(min, Math.min(max, Math.trunc(value)));
8520
+ }
8521
+ function isoDateDaysAgo(days) {
8522
+ const d = new Date(Date.now() - (days - 1) * 24 * 60 * 60 * 1e3);
8523
+ return d.toISOString().slice(0, 10);
8524
+ }
8525
+ function todayIsoDate() {
8526
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
8527
+ }
8528
+ function compactOverview(overview) {
8529
+ return {
8530
+ project: overview.project,
8531
+ contextLabel: overview.contextLabel,
8532
+ dateRangeLabel: overview.dateRangeLabel,
8533
+ latestRun: overview.latestRun,
8534
+ health: overview.health,
8535
+ scores: overview.scores,
8536
+ queryCounts: overview.queryCounts,
8537
+ providers: overview.providers,
8538
+ citationMovement: overview.citationMovement,
8539
+ mentionMovement: overview.mentionMovement,
8540
+ movementComparison: overview.movementComparison,
8541
+ topInsights: overview.topInsights?.slice(0, 5) ?? [],
8542
+ attentionItems: overview.attentionItems?.slice(0, 8) ?? [],
8543
+ competitors: overview.competitors?.slice(0, 12) ?? []
8544
+ };
8545
+ }
8546
+ function compactCampaigns(response, limit) {
8547
+ return {
8548
+ campaignCount: response.campaigns.length,
8549
+ campaigns: response.campaigns.slice(0, limit).map((campaign) => ({
8550
+ id: campaign.id,
8551
+ name: campaign.name,
8552
+ status: campaign.status,
8553
+ biddingType: campaign.biddingType,
8554
+ dailySpendLimitMicros: campaign.dailySpendLimitMicros,
8555
+ lifetimeSpendLimitMicros: campaign.lifetimeSpendLimitMicros,
8556
+ adGroupCount: campaign.adGroups.length,
8557
+ adGroups: campaign.adGroups.slice(0, 8).map((group) => ({
8558
+ id: group.id,
8559
+ name: group.name,
8560
+ status: group.status,
8561
+ billingEventType: group.billingEventType,
8562
+ maxBidMicros: group.maxBidMicros,
8563
+ contextHintCount: group.contextHints.length,
8564
+ contextHints: group.contextHints.slice(0, 3),
8565
+ adCount: group.ads.length,
8566
+ ads: group.ads.slice(0, 4).map((ad) => ({
8567
+ id: ad.id,
8568
+ name: ad.name,
8569
+ status: ad.status,
8570
+ reviewStatus: ad.reviewStatus,
8571
+ creative: ad.creative ? {
8572
+ type: ad.creative.type,
8573
+ title: ad.creative.title,
8574
+ body: ad.creative.body,
8575
+ targetUrl: ad.creative.targetUrl
8576
+ } : ad.creative
8577
+ }))
8578
+ }))
8579
+ }))
8580
+ };
8581
+ }
8582
+ function compactInsights(response, rowLimit) {
8583
+ return {
8584
+ currencyCode: response.currencyCode,
8585
+ rowCount: response.rows.length,
8586
+ rows: response.rows.slice(-rowLimit)
8587
+ };
8588
+ }
8589
+ function buildAdsOperatorContextTool(ctx) {
8590
+ return {
8591
+ name: AERO_ADS_OPERATOR_CONTEXT_TOOL_NAME,
8592
+ label: "Get ads operator context",
8593
+ 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.",
8594
+ parameters: Type3.Object({
8595
+ windowDays: Type3.Optional(Type3.Integer({
8596
+ minimum: 1,
8597
+ maximum: 90,
8598
+ description: "Lookback window for paid rollup rows. Default 30 days."
8599
+ })),
8600
+ campaignLimit: Type3.Optional(Type3.Integer({
8601
+ minimum: 1,
8602
+ maximum: 25,
8603
+ description: "Max campaign snapshots to include. Default 8."
8604
+ })),
8605
+ insightRowLimit: Type3.Optional(Type3.Integer({
8606
+ minimum: 1,
8607
+ maximum: 100,
8608
+ description: "Max paid rollup rows to include per level. Default 40."
8609
+ }))
8610
+ }),
8611
+ execute: async (_toolCallId, rawParams) => {
8612
+ const params = rawParams;
8613
+ const windowDays = clampInteger(params.windowDays, 30, 1, 90);
8614
+ const campaignLimit = clampInteger(params.campaignLimit, 8, 1, 25);
8615
+ const insightRowLimit = clampInteger(params.insightRowLimit, 40, 1, 100);
8616
+ const from = isoDateDaysAgo(windowDays);
8617
+ const to = todayIsoDate();
8618
+ const [
8619
+ overview,
8620
+ adsStatus,
8621
+ adsSummary,
8622
+ campaigns,
8623
+ campaignInsights,
8624
+ adGroupInsights,
8625
+ doctor,
8626
+ memory
8627
+ ] = await Promise.all([
8628
+ readSection(() => ctx.client.getProjectOverview(ctx.projectName)),
8629
+ readSection(() => ctx.client.getAdsStatus(ctx.projectName)),
8630
+ readSection(() => ctx.client.getAdsSummary(ctx.projectName)),
8631
+ readSection(async () => compactCampaigns(await ctx.client.getAdsCampaigns(ctx.projectName), campaignLimit)),
8632
+ readSection(
8633
+ async () => compactInsights(
8634
+ await ctx.client.getAdsInsights(ctx.projectName, { level: "campaign", from, to }),
8635
+ insightRowLimit
8636
+ )
8637
+ ),
8638
+ readSection(
8639
+ async () => compactInsights(
8640
+ await ctx.client.getAdsInsights(ctx.projectName, { level: "ad_group", from, to }),
8641
+ insightRowLimit
8642
+ )
8643
+ ),
8644
+ readSection(() => ctx.client.runDoctor({ project: ctx.projectName, checkIds: ["ads.*"] })),
8645
+ readSection(async () => {
8646
+ const result = await ctx.client.listAgentMemory(ctx.projectName);
8647
+ return { entries: result.entries.slice(0, 10) };
8648
+ })
8649
+ ]);
8650
+ return toolResult({
8651
+ project: ctx.projectName,
8652
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
8653
+ window: { days: windowDays, from, to },
8654
+ overview: overview.status === "ok" && overview.data ? { status: "ok", data: compactOverview(overview.data) } : overview,
8655
+ ads: {
8656
+ status: adsStatus,
8657
+ summary: adsSummary,
8658
+ campaigns,
8659
+ insights: {
8660
+ campaign: campaignInsights,
8661
+ adGroup: adGroupInsights
8662
+ }
8663
+ },
8664
+ doctor,
8665
+ memory
8666
+ });
8667
+ }
8668
+ };
8669
+ }
8441
8670
  function buildReadTools(ctx) {
8442
8671
  return buildMcpAgentTools(canonryMcpTools, ctx, { readOnly: true });
8443
8672
  }
8444
8673
  function buildAllTools(ctx) {
8445
8674
  return buildMcpAgentTools(canonryMcpTools, ctx);
8446
8675
  }
8676
+ function buildAdsOperatorTools(ctx, opts = {}) {
8677
+ const mcpTools = buildMcpAgentTools(canonryMcpTools, ctx, {
8678
+ readOnly: opts.scope === AeroToolScopes.readOnly,
8679
+ includeNames: AERO_ADS_OPERATOR_MCP_TOOL_NAMES
8680
+ });
8681
+ return [buildAdsOperatorContextTool(ctx), ...mcpTools];
8682
+ }
8683
+ function buildAeroStateTools(ctx, opts = {}) {
8684
+ const scope = opts.scope ?? AeroToolScopes.all;
8685
+ const profile = opts.profile ?? AeroToolProfiles.default;
8686
+ if (profile === AeroToolProfiles.adsOperator) {
8687
+ return buildAdsOperatorTools(ctx, { scope });
8688
+ }
8689
+ return scope === AeroToolScopes.readOnly ? buildReadTools(ctx) : buildAllTools(ctx);
8690
+ }
8691
+
8692
+ // src/agent/llm-usage.ts
8693
+ import crypto18 from "crypto";
8694
+ var AeroLlmUsageFeatures = {
8695
+ turn: "aero.turn"
8696
+ };
8697
+ var AERO_PROMPT_FAMILY = "aero";
8698
+ var AERO_PROMPT_VERSION = "aero-system-v1";
8699
+ function dollarsToMillicents(dollars) {
8700
+ if (!Number.isFinite(dollars) || dollars <= 0) return 0;
8701
+ return Math.round(dollars * 1e5);
8702
+ }
8703
+ function nonNegativeInteger(value) {
8704
+ if (!Number.isFinite(value) || value === void 0 || value <= 0) return 0;
8705
+ return Math.round(value);
8706
+ }
8707
+ function recordLlmUsageEvent(args) {
8708
+ try {
8709
+ const usage = args.message.usage;
8710
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8711
+ args.db.insert(llmUsageEvents).values({
8712
+ id: crypto18.randomUUID(),
8713
+ projectId: args.projectId,
8714
+ runId: args.runId,
8715
+ agentSessionId: args.agentSessionId,
8716
+ feature: args.feature,
8717
+ provider: args.message.provider,
8718
+ model: args.message.model,
8719
+ responseId: args.message.responseId,
8720
+ inputTokens: nonNegativeInteger(usage.input),
8721
+ outputTokens: nonNegativeInteger(usage.output),
8722
+ cacheReadTokens: nonNegativeInteger(usage.cacheRead),
8723
+ cacheWriteTokens: nonNegativeInteger(usage.cacheWrite),
8724
+ totalTokens: nonNegativeInteger(usage.totalTokens),
8725
+ costMillicents: dollarsToMillicents(usage.cost.total),
8726
+ promptFamily: args.promptFamily,
8727
+ promptVersion: args.promptVersion,
8728
+ metadata: args.metadata,
8729
+ createdAt: now
8730
+ }).run();
8731
+ } catch {
8732
+ }
8733
+ }
8734
+
8735
+ // src/agent/prompt-cache.ts
8736
+ var AERO_MEMORY_SEPARATOR = "\n\n---\n\n";
8737
+ var AERO_MEMORY_BLOCK_PREFIX = `${AERO_MEMORY_SEPARATOR}<memory>
8738
+ `;
8739
+ function isRecord(value) {
8740
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
8741
+ }
8742
+ function isTextSystemBlock(value) {
8743
+ return isRecord(value) && value.type === "text" && typeof value.text === "string";
8744
+ }
8745
+ function splitAeroHydratedSystemPrompt(systemPrompt) {
8746
+ const idx = systemPrompt.lastIndexOf(AERO_MEMORY_BLOCK_PREFIX);
8747
+ if (idx < 0) return { basePrompt: systemPrompt };
8748
+ return {
8749
+ basePrompt: systemPrompt.slice(0, idx).trimEnd(),
8750
+ memoryBlock: systemPrompt.slice(idx + AERO_MEMORY_SEPARATOR.length)
8751
+ };
8752
+ }
8753
+ function splitAeroAnthropicSystemCachePayload(payload, model) {
8754
+ if (model.api !== "anthropic-messages") return void 0;
8755
+ if (!isRecord(payload) || !Array.isArray(payload.system)) return void 0;
8756
+ let changed = false;
8757
+ const nextSystem = [];
8758
+ for (const block of payload.system) {
8759
+ if (!isTextSystemBlock(block)) {
8760
+ nextSystem.push(block);
8761
+ continue;
8762
+ }
8763
+ const split = splitAeroHydratedSystemPrompt(block.text);
8764
+ if (!split.memoryBlock) {
8765
+ nextSystem.push(block);
8766
+ continue;
8767
+ }
8768
+ const memoryBlock = {
8769
+ ...block,
8770
+ text: split.memoryBlock
8771
+ };
8772
+ delete memoryBlock.cache_control;
8773
+ nextSystem.push({
8774
+ ...block,
8775
+ text: split.basePrompt
8776
+ });
8777
+ nextSystem.push(memoryBlock);
8778
+ changed = true;
8779
+ }
8780
+ if (!changed) return void 0;
8781
+ return {
8782
+ ...payload,
8783
+ system: nextSystem
8784
+ };
8785
+ }
8786
+
8787
+ // src/agent/tool-usage.ts
8788
+ import crypto19 from "crypto";
8789
+ var AeroToolEventStatuses = {
8790
+ success: "success",
8791
+ error: "error"
8792
+ };
8793
+ function jsonBytes(value) {
8794
+ try {
8795
+ return Buffer.byteLength(JSON.stringify(value), "utf8");
8796
+ } catch {
8797
+ return 0;
8798
+ }
8799
+ }
8800
+ function nonNegativeInteger2(value) {
8801
+ if (!Number.isFinite(value) || value === void 0 || value <= 0) return 0;
8802
+ return Math.round(value);
8803
+ }
8804
+ function resultTextChars(content) {
8805
+ let total = 0;
8806
+ for (const block of content) {
8807
+ if (block.type === "text") {
8808
+ total += block.text.length;
8809
+ } else {
8810
+ total += block.data.length;
8811
+ }
8812
+ }
8813
+ return total;
8814
+ }
8815
+ function createAeroToolUsageHooks(args) {
8816
+ const startedAt = /* @__PURE__ */ new Map();
8817
+ return {
8818
+ beforeToolCall: async ({ toolCall }) => {
8819
+ startedAt.set(toolCall.id, Date.now());
8820
+ return void 0;
8821
+ },
8822
+ afterToolCall: async ({ assistantMessage, toolCall, args: toolArgs, result, isError, context }) => {
8823
+ const start = startedAt.get(toolCall.id);
8824
+ startedAt.delete(toolCall.id);
8825
+ recordAgentToolEvent({
8826
+ db: args.db,
8827
+ projectId: args.projectId,
8828
+ agentSessionId: args.agentSessionId,
8829
+ toolCallId: toolCall.id,
8830
+ toolName: toolCall.name,
8831
+ assistantResponseId: assistantMessage.responseId,
8832
+ provider: assistantMessage.provider,
8833
+ model: assistantMessage.model,
8834
+ status: isError ? AeroToolEventStatuses.error : AeroToolEventStatuses.success,
8835
+ durationMs: start === void 0 ? 0 : Date.now() - start,
8836
+ argsBytes: jsonBytes(toolArgs),
8837
+ resultTextChars: resultTextChars(result.content),
8838
+ resultBytes: jsonBytes(result),
8839
+ metadata: {
8840
+ ...args.metadata,
8841
+ toolCount: context.tools?.length ?? 0
8842
+ }
8843
+ });
8844
+ return void 0;
8845
+ }
8846
+ };
8847
+ }
8848
+ function recordAgentToolEvent(args) {
8849
+ try {
8850
+ args.db.insert(agentToolEvents).values({
8851
+ id: crypto19.randomUUID(),
8852
+ projectId: args.projectId,
8853
+ agentSessionId: args.agentSessionId,
8854
+ toolCallId: args.toolCallId,
8855
+ toolName: args.toolName,
8856
+ assistantResponseId: args.assistantResponseId,
8857
+ provider: args.provider,
8858
+ model: args.model,
8859
+ status: args.status,
8860
+ durationMs: nonNegativeInteger2(args.durationMs),
8861
+ argsBytes: nonNegativeInteger2(args.argsBytes),
8862
+ resultTextChars: nonNegativeInteger2(args.resultTextChars),
8863
+ resultBytes: nonNegativeInteger2(args.resultBytes),
8864
+ metadata: args.metadata,
8865
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
8866
+ }).run();
8867
+ } catch {
8868
+ }
8869
+ }
8447
8870
 
8448
8871
  // src/agent/session.ts
8449
8872
  var builtinsRegistered = false;
@@ -8502,20 +8925,30 @@ function resolveAeroModel(provider, modelId) {
8502
8925
  function buildApiKeyResolver(config) {
8503
8926
  return (piAiProvider) => resolveApiKeyFor(piAiProvider, config);
8504
8927
  }
8928
+ function buildAeroProviderSessionId(opts) {
8929
+ return `canonry:aero:${opts.agentSessionId ?? opts.projectId ?? opts.projectName}`;
8930
+ }
8505
8931
  function createAeroSession(opts) {
8506
8932
  const systemPrompt = opts.systemPromptOverride ?? loadAeroSystemPrompt();
8507
8933
  const provider = opts.provider ?? detectAgentProvider(opts.config);
8508
8934
  if (!provider) throw new Error(missingProviderMessage());
8509
8935
  const model = resolveAeroModel(provider, opts.modelId);
8510
- const toolScope = opts.toolScope ?? "all";
8936
+ const toolScope = opts.toolScope ?? AeroToolScopes.all;
8937
+ const toolProfile = opts.toolProfile ?? AeroToolProfiles.default;
8511
8938
  const toolCtx = {
8512
8939
  client: opts.client,
8513
8940
  projectName: opts.projectName
8514
8941
  };
8515
- const stateTools = toolScope === "read-only" ? buildReadTools(toolCtx) : buildAllTools(toolCtx);
8942
+ const stateTools = buildAeroStateTools(toolCtx, { scope: toolScope, profile: toolProfile });
8516
8943
  const defaultTools = [...stateTools, ...buildSkillDocTools()];
8517
8944
  const tools = opts.tools ?? defaultTools;
8518
- return new Agent({
8945
+ const toolUsageHooks = opts.db ? createAeroToolUsageHooks({
8946
+ db: opts.db,
8947
+ projectId: opts.projectId,
8948
+ agentSessionId: opts.agentSessionId,
8949
+ metadata: { projectName: opts.projectName }
8950
+ }) : {};
8951
+ const agent = new Agent({
8519
8952
  initialState: {
8520
8953
  systemPrompt,
8521
8954
  model,
@@ -8523,8 +8956,29 @@ function createAeroSession(opts) {
8523
8956
  ...opts.initialMessages ? { messages: opts.initialMessages } : {}
8524
8957
  },
8525
8958
  streamFn: opts.streamFn,
8959
+ sessionId: buildAeroProviderSessionId(opts),
8960
+ onPayload: splitAeroAnthropicSystemCachePayload,
8961
+ ...toolUsageHooks,
8526
8962
  getApiKey: buildApiKeyResolver(opts.config)
8527
8963
  });
8964
+ const telemetryDb = opts.db;
8965
+ if (telemetryDb) {
8966
+ agent.subscribe((event) => {
8967
+ if (event.type !== "turn_end") return;
8968
+ if (event.message.role !== "assistant") return;
8969
+ recordLlmUsageEvent({
8970
+ db: telemetryDb,
8971
+ projectId: opts.projectId,
8972
+ agentSessionId: opts.agentSessionId,
8973
+ feature: AeroLlmUsageFeatures.turn,
8974
+ promptFamily: AERO_PROMPT_FAMILY,
8975
+ promptVersion: AERO_PROMPT_VERSION,
8976
+ message: event.message,
8977
+ metadata: { projectName: opts.projectName, toolCount: agent.state.tools.length }
8978
+ });
8979
+ });
8980
+ }
8981
+ return agent;
8528
8982
  }
8529
8983
  function resolveSessionProviderAndModel(config, opts) {
8530
8984
  const provider = opts?.provider ?? detectAgentProvider(config);
@@ -8534,7 +8988,7 @@ function resolveSessionProviderAndModel(config, opts) {
8534
8988
  }
8535
8989
 
8536
8990
  // src/agent/remote-mcp.ts
8537
- import { Type as Type3 } from "@sinclair/typebox";
8991
+ import { Type as Type4 } from "@sinclair/typebox";
8538
8992
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
8539
8993
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
8540
8994
  var log18 = createLogger("RemoteMcp");
@@ -8561,7 +9015,7 @@ function isReadOnly(tool) {
8561
9015
  return tool.annotations?.readOnlyHint === true;
8562
9016
  }
8563
9017
  function adaptRemoteTool(client, tool) {
8564
- const parameters = Type3.Unsafe(
9018
+ const parameters = Type4.Unsafe(
8565
9019
  tool.inputSchema ?? { type: "object", properties: {} }
8566
9020
  );
8567
9021
  const execute = async (_toolCallId, params) => {
@@ -8621,7 +9075,7 @@ async function loadExternalMcpTools(servers, opts = {}) {
8621
9075
  }
8622
9076
 
8623
9077
  // src/agent/memory-store.ts
8624
- import crypto18 from "crypto";
9078
+ import crypto20 from "crypto";
8625
9079
  import { and as and13, desc as desc6, eq as eq17, like, sql as sql5 } from "drizzle-orm";
8626
9080
  var COMPACTION_KEY_PREFIX = "compaction:";
8627
9081
  var COMPACTION_NOTES_PER_SESSION = 3;
@@ -8650,7 +9104,7 @@ function upsertMemoryEntry(db, args) {
8650
9104
  throw new Error(`memory key prefix "${COMPACTION_KEY_PREFIX}" is reserved for compaction notes`);
8651
9105
  }
8652
9106
  const now = (/* @__PURE__ */ new Date()).toISOString();
8653
- const id = crypto18.randomUUID();
9107
+ const id = crypto20.randomUUID();
8654
9108
  db.insert(agentMemory).values({
8655
9109
  id,
8656
9110
  projectId: args.projectId,
@@ -8687,7 +9141,7 @@ function writeCompactionNote(db, args) {
8687
9141
  }
8688
9142
  const now = (/* @__PURE__ */ new Date()).toISOString();
8689
9143
  const key = `${COMPACTION_KEY_PREFIX}${args.sessionId}:${now}`;
8690
- const id = crypto18.randomUUID();
9144
+ const id = crypto20.randomUUID();
8691
9145
  let inserted;
8692
9146
  db.transaction((tx) => {
8693
9147
  tx.insert(agentMemory).values({
@@ -8854,6 +9308,8 @@ var SessionRegistry = class {
8854
9308
  pending = /* @__PURE__ */ new Map();
8855
9309
  /** Last tool scope used on the live Agent for a project. Read in getOrCreate to know when to swap. */
8856
9310
  scopes = /* @__PURE__ */ new Map();
9311
+ /** Last profile used on the live Agent for a project. Paired with scopes when rebuilding tools. */
9312
+ profiles = /* @__PURE__ */ new Map();
8857
9313
  /** Cached resolved project id per project name, used so alignScope can rebuild tool context without a DB roundtrip. */
8858
9314
  projectIds = /* @__PURE__ */ new Map();
8859
9315
  /**
@@ -8945,9 +9401,14 @@ var SessionRegistry = class {
8945
9401
  modelId: effectiveModelId,
8946
9402
  systemPromptOverride: this.buildHydratedSystemPrompt(projectId, row.systemPrompt),
8947
9403
  initialMessages: persistedMessages,
8948
- toolScope: preferences?.toolScope
9404
+ toolScope: preferences?.toolScope,
9405
+ toolProfile: preferences?.toolProfile,
9406
+ db: this.opts.db,
9407
+ projectId,
9408
+ agentSessionId: row.id
8949
9409
  });
8950
- this.scopes.set(projectName, preferences?.toolScope ?? "all");
9410
+ this.scopes.set(projectName, preferences?.toolScope ?? AeroToolScopes.all);
9411
+ this.profiles.set(projectName, preferences?.toolProfile ?? AeroToolProfiles.default);
8951
9412
  this.projectIds.set(projectName, projectId);
8952
9413
  if (queued.length > 0) {
8953
9414
  this.appendPending(projectName, queued);
@@ -8959,6 +9420,7 @@ var SessionRegistry = class {
8959
9420
  }
8960
9421
  const { provider, modelId } = resolveSessionProviderAndModel(this.opts.config, preferences);
8961
9422
  const systemPrompt = loadAeroSystemPrompt();
9423
+ const sessionId = crypto21.randomUUID();
8962
9424
  const agent = createAeroSession({
8963
9425
  projectName,
8964
9426
  client: this.opts.client,
@@ -8968,11 +9430,17 @@ var SessionRegistry = class {
8968
9430
  // Hydrate on the fresh path too — a brand-new session may still see
8969
9431
  // notes if they were seeded via CLI/API before the first prompt.
8970
9432
  systemPromptOverride: this.buildHydratedSystemPrompt(projectId, systemPrompt),
8971
- toolScope: preferences?.toolScope
9433
+ toolScope: preferences?.toolScope,
9434
+ toolProfile: preferences?.toolProfile,
9435
+ db: this.opts.db,
9436
+ projectId,
9437
+ agentSessionId: sessionId
8972
9438
  });
8973
- this.scopes.set(projectName, preferences?.toolScope ?? "all");
9439
+ this.scopes.set(projectName, preferences?.toolScope ?? AeroToolScopes.all);
9440
+ this.profiles.set(projectName, preferences?.toolProfile ?? AeroToolProfiles.default);
8974
9441
  this.projectIds.set(projectName, projectId);
8975
9442
  this.insertRow({
9443
+ id: sessionId,
8976
9444
  projectId,
8977
9445
  // Persist the raw (unhydrated) prompt so the DB remains canonical —
8978
9446
  // the `<memory>` block is rebuilt from the notes table on every load.
@@ -9049,8 +9517,9 @@ ${lines.join("\n")}
9049
9517
  * Busy-check runs FIRST, before any state mutation — if two requests race
9050
9518
  * on the same project, one gets the 409 and the other's in-flight turn is
9051
9519
  * 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).
9520
+ * - align `state.tools` to the requested scope/profile (CLI full vs
9521
+ * dashboard read-only share the same cached Agent; each request
9522
+ * re-scopes it).
9054
9523
  * - align `state.model` when the caller passes `provider` or `modelId`,
9055
9524
  * honoring `--provider` / `--model` on hot sessions (not just on
9056
9525
  * fresh/hydrated construction).
@@ -9063,7 +9532,10 @@ ${lines.join("\n")}
9063
9532
  if (agent.state.isStreaming) {
9064
9533
  throw agentBusy(projectName);
9065
9534
  }
9066
- this.alignScope(projectName, agent, preferences?.toolScope ?? "all");
9535
+ this.alignToolSurface(projectName, agent, {
9536
+ scope: preferences?.toolScope ?? AeroToolScopes.all,
9537
+ profile: preferences?.toolProfile ?? AeroToolProfiles.default
9538
+ });
9067
9539
  if (preferences?.provider || preferences?.modelId) {
9068
9540
  this.alignModel(projectName, agent, preferences);
9069
9541
  }
@@ -9126,14 +9598,17 @@ ${lines.join("\n")}
9126
9598
  });
9127
9599
  }
9128
9600
  }
9129
- alignScope(projectName, agent, wantScope) {
9130
- if (this.scopes.get(projectName) === wantScope) return;
9601
+ alignToolSurface(projectName, agent, want) {
9602
+ if (this.scopes.get(projectName) === want.scope && this.profiles.get(projectName) === want.profile) {
9603
+ return;
9604
+ }
9131
9605
  const projectId = this.projectIds.get(projectName) ?? this.resolveProjectId(projectName);
9132
9606
  this.projectIds.set(projectName, projectId);
9133
9607
  const toolCtx = { client: this.opts.client, projectName };
9134
- const stateTools = wantScope === "read-only" ? buildReadTools(toolCtx) : buildAllTools(toolCtx);
9608
+ const stateTools = buildAeroStateTools(toolCtx, want);
9135
9609
  agent.state.tools = [...stateTools, ...buildSkillDocTools()];
9136
- this.scopes.set(projectName, wantScope);
9610
+ this.scopes.set(projectName, want.scope);
9611
+ this.profiles.set(projectName, want.profile);
9137
9612
  }
9138
9613
  alignModel(projectName, agent, preferences) {
9139
9614
  const projectId = this.tryResolveProjectId(projectName);
@@ -9206,8 +9681,9 @@ ${lines.join("\n")}
9206
9681
  try {
9207
9682
  let agent;
9208
9683
  try {
9209
- const scope = this.scopes.get(projectName) ?? "read-only";
9210
- agent = await this.acquireForTurn(projectName, { toolScope: scope });
9684
+ const scope = this.scopes.get(projectName) ?? AeroToolScopes.readOnly;
9685
+ const profile = this.profiles.get(projectName) ?? AeroToolProfiles.default;
9686
+ agent = await this.acquireForTurn(projectName, { toolScope: scope, toolProfile: profile });
9211
9687
  } catch (err) {
9212
9688
  if (err.code === "AGENT_BUSY") return;
9213
9689
  throw err;
@@ -9242,6 +9718,7 @@ ${lines.join("\n")}
9242
9718
  this.live.delete(projectName);
9243
9719
  this.pending.delete(projectName);
9244
9720
  this.scopes.delete(projectName);
9721
+ this.profiles.delete(projectName);
9245
9722
  this.projectIds.delete(projectName);
9246
9723
  }
9247
9724
  /** Evict every live Agent. Durable state in DB is untouched. */
@@ -9322,7 +9799,7 @@ ${lines.join("\n")}
9322
9799
  insertRow(params) {
9323
9800
  const now = (/* @__PURE__ */ new Date()).toISOString();
9324
9801
  this.opts.db.insert(agentSessions).values({
9325
- id: crypto19.randomUUID(),
9802
+ id: params.id ?? crypto21.randomUUID(),
9326
9803
  projectId: params.projectId,
9327
9804
  systemPrompt: params.systemPrompt,
9328
9805
  modelProvider: params.provider ?? params.modelProvider ?? AgentProviderIds.claude,
@@ -9381,13 +9858,16 @@ function registerAgentRoutes(app, opts) {
9381
9858
  );
9382
9859
  app.post("/projects/:name/agent/prompt", async (request, reply) => {
9383
9860
  const project = resolveProject(opts.db, request.params.name);
9384
- const promptText = (request.body?.prompt ?? "").trim();
9861
+ const body = request.body;
9862
+ const promptText = (body?.prompt ?? "").trim();
9385
9863
  if (!promptText) throw validationError('"prompt" is required');
9386
- const requestedScope = request.body?.scope === "all" ? "all" : "read-only";
9864
+ const requestedScope = body?.scope === AeroToolScopes.all ? AeroToolScopes.all : AeroToolScopes.readOnly;
9865
+ const requestedProfile = isAeroToolProfile(body?.profile) ? body.profile : AeroToolProfiles.default;
9387
9866
  const agent = await opts.sessionRegistry.acquireForTurn(project.name, {
9388
- provider: request.body?.provider,
9389
- modelId: request.body?.modelId,
9390
- toolScope: requestedScope
9867
+ provider: body?.provider,
9868
+ modelId: body?.modelId,
9869
+ toolScope: requestedScope,
9870
+ toolProfile: requestedProfile
9391
9871
  });
9392
9872
  reply.raw.writeHead(200, {
9393
9873
  "Content-Type": "text/event-stream",
@@ -9500,7 +9980,7 @@ Output requirements:
9500
9980
  - Final bullet: the expected outcome if executed (recovered citations, ranking lift, etc.).
9501
9981
  - No preamble, no closing pleasantries, no markdown headers.
9502
9982
  - Maximum 600 characters total. Be dense.`;
9503
- function dollarsToMillicents(dollars) {
9983
+ function dollarsToMillicents2(dollars) {
9504
9984
  if (!Number.isFinite(dollars) || dollars <= 0) return 0;
9505
9985
  return Math.round(dollars * 1e5);
9506
9986
  }
@@ -9607,7 +10087,7 @@ function createRecommendationExplainer(opts) {
9607
10087
  provider,
9608
10088
  model: model.id,
9609
10089
  responseText: text,
9610
- costMillicents: dollarsToMillicents(resp.usage.cost.total)
10090
+ costMillicents: dollarsToMillicents2(resp.usage.cost.total)
9611
10091
  };
9612
10092
  };
9613
10093
  }
@@ -9685,7 +10165,7 @@ Your previous reply was not valid JSON. Return ONLY a JSON object with exactly t
9685
10165
  provider,
9686
10166
  model: model.id,
9687
10167
  brief,
9688
- costMillicents: dollarsToMillicents(totalCostDollars)
10168
+ costMillicents: dollarsToMillicents2(totalCostDollars)
9689
10169
  };
9690
10170
  }
9691
10171
  }
@@ -10447,14 +10927,14 @@ function summarizeProviderConfig(config) {
10447
10927
  };
10448
10928
  }
10449
10929
  function hashApiKey(key) {
10450
- return crypto20.createHash("sha256").update(key).digest("hex");
10930
+ return crypto22.createHash("sha256").update(key).digest("hex");
10451
10931
  }
10452
10932
  var DASHBOARD_SCRYPT_KEYLEN = 64;
10453
10933
  var DASHBOARD_SCRYPT_COST = 1 << 15;
10454
10934
  var DASHBOARD_SCRYPT_MAXMEM = 64 * 1024 * 1024;
10455
10935
  function hashDashboardPassword(password) {
10456
- const salt = crypto20.randomBytes(16);
10457
- const derived = crypto20.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
10936
+ const salt = crypto22.randomBytes(16);
10937
+ const derived = crypto22.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
10458
10938
  N: DASHBOARD_SCRYPT_COST,
10459
10939
  maxmem: DASHBOARD_SCRYPT_MAXMEM
10460
10940
  });
@@ -10475,14 +10955,14 @@ function verifyDashboardPassword(password, storedHash) {
10475
10955
  } catch {
10476
10956
  return { ok: false, needsRehash: false };
10477
10957
  }
10478
- const derived = crypto20.scryptSync(password, salt, expected.length, {
10958
+ const derived = crypto22.scryptSync(password, salt, expected.length, {
10479
10959
  N: DASHBOARD_SCRYPT_COST,
10480
10960
  maxmem: DASHBOARD_SCRYPT_MAXMEM
10481
10961
  });
10482
10962
  if (derived.length !== expected.length)
10483
10963
  return { ok: false, needsRehash: false };
10484
10964
  return {
10485
- ok: crypto20.timingSafeEqual(derived, expected),
10965
+ ok: crypto22.timingSafeEqual(derived, expected),
10486
10966
  needsRehash: false
10487
10967
  };
10488
10968
  }
@@ -10491,7 +10971,7 @@ function verifyDashboardPassword(password, storedHash) {
10491
10971
  const expected = Buffer.from(storedHash, "hex");
10492
10972
  if (candidate.length !== expected.length)
10493
10973
  return { ok: false, needsRehash: false };
10494
- const ok = crypto20.timingSafeEqual(candidate, expected);
10974
+ const ok = crypto22.timingSafeEqual(candidate, expected);
10495
10975
  return { ok, needsRehash: ok };
10496
10976
  }
10497
10977
  return { ok: false, needsRehash: false };
@@ -10961,7 +11441,7 @@ async function createServer(opts) {
10961
11441
  return removed;
10962
11442
  }
10963
11443
  };
10964
- const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto20.randomBytes(32).toString("hex");
11444
+ const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto22.randomBytes(32).toString("hex");
10965
11445
  const googleConnectionStore = {
10966
11446
  listConnections: (domain) => listGoogleConnections(opts.config, domain),
10967
11447
  getConnection: (domain, connectionType) => getGoogleConnection(opts.config, domain, connectionType),
@@ -11022,7 +11502,7 @@ async function createServer(opts) {
11022
11502
  if (!existing) {
11023
11503
  const prefix = opts.config.apiKey.slice(0, 12);
11024
11504
  opts.db.insert(apiKeys).values({
11025
- id: `key_${crypto20.randomBytes(8).toString("hex")}`,
11505
+ id: `key_${crypto22.randomBytes(8).toString("hex")}`,
11026
11506
  name: "default",
11027
11507
  keyHash,
11028
11508
  keyPrefix: prefix,
@@ -11046,7 +11526,7 @@ async function createServer(opts) {
11046
11526
  };
11047
11527
  const createSession = (apiKeyId) => {
11048
11528
  pruneExpiredSessions();
11049
- const sessionId = crypto20.randomBytes(32).toString("hex");
11529
+ const sessionId = crypto22.randomBytes(32).toString("hex");
11050
11530
  sessions.set(sessionId, {
11051
11531
  apiKeyId,
11052
11532
  expiresAt: Date.now() + SESSION_TTL_MS
@@ -11346,7 +11826,7 @@ async function createServer(opts) {
11346
11826
  deps: {
11347
11827
  enqueueAutoExtract: ({ projectId, release: r }) => {
11348
11828
  const now = (/* @__PURE__ */ new Date()).toISOString();
11349
- const runId = crypto20.randomUUID();
11829
+ const runId = crypto22.randomUUID();
11350
11830
  opts.db.insert(runs).values({
11351
11831
  id: runId,
11352
11832
  projectId,
@@ -11564,7 +12044,7 @@ async function createServer(opts) {
11564
12044
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
11565
12045
  opts.db.insert(auditLog).values(
11566
12046
  targetProjectIds.map((projectId) => ({
11567
- id: crypto20.randomUUID(),
12047
+ id: crypto22.randomUUID(),
11568
12048
  projectId,
11569
12049
  actor: "api",
11570
12050
  action: existing ? "provider.updated" : "provider.created",
@@ -11896,5 +12376,7 @@ export {
11896
12376
  checkLatestVersionForCli,
11897
12377
  listAgentProviders,
11898
12378
  coerceAgentProvider,
12379
+ AERO_TOOL_PROFILES,
12380
+ isAeroToolProfile,
11899
12381
  createServer
11900
12382
  };