@ainyc/canonry 4.100.1 → 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.
- package/assets/assets/{BacklinksPage-v2RC8wlg.js → BacklinksPage-aysk_iGy.js} +1 -1
- package/assets/assets/{ChartPrimitives-B74e5pqn.js → ChartPrimitives-CmKqxndA.js} +1 -1
- package/assets/assets/ProjectPage-dduZrrBr.js +7 -0
- package/assets/assets/{RunRow-BmVPjO-m.js → RunRow-Ba77hJ4d.js} +1 -1
- package/assets/assets/{RunsPage-CK5atXvL.js → RunsPage-D3IAdQMB.js} +1 -1
- package/assets/assets/{SettingsPage-DarPFUPl.js → SettingsPage-Y54JtLw-.js} +1 -1
- package/assets/assets/{TrafficPage-Ddnn_WqU.js → TrafficPage-DawzPWdt.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-g08SKnwZ.js → TrafficSourceDetailPage-DAhQwFVb.js} +1 -1
- package/assets/assets/{arrow-left-D39Vxasb.js → arrow-left-NsJbgqmV.js} +1 -1
- package/assets/assets/{extract-error-message-BiNWqBeh.js → extract-error-message-u00ggxeK.js} +1 -1
- package/assets/assets/{index-Dd4J1VhB.js → index-4YArcfwj.js} +64 -64
- package/assets/assets/{trash-2-xh6gsnap.js → trash-2-BBdbbe60.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-IDI6LMYL.js → chunk-2YPVK75M.js} +6 -2
- package/dist/{chunk-YBIT5O62.js → chunk-AV3DHH6B.js} +142 -4
- package/dist/{chunk-JF7EXHGA.js → chunk-HUL5FUBD.js} +6 -0
- package/dist/{chunk-ALJSES3X.js → chunk-O3SFVGDF.js} +531 -49
- package/dist/cli.js +26 -8
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-TWCEKVKZ.js → intelligence-service-R6QJ3KC2.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +6 -6
- 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-
|
|
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-
|
|
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-
|
|
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
|
|
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-
|
|
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
|
|
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
|
-
|
|
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 ??
|
|
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 =
|
|
8942
|
+
const stateTools = buildAeroStateTools(toolCtx, { scope: toolScope, profile: toolProfile });
|
|
8516
8943
|
const defaultTools = [...stateTools, ...buildSkillDocTools()];
|
|
8517
8944
|
const tools = opts.tools ?? defaultTools;
|
|
8518
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 ??
|
|
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 ??
|
|
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
|
|
9053
|
-
* read-only share the same cached Agent; each request
|
|
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.
|
|
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
|
-
|
|
9130
|
-
if (this.scopes.get(projectName) ===
|
|
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 =
|
|
9608
|
+
const stateTools = buildAeroStateTools(toolCtx, want);
|
|
9135
9609
|
agent.state.tools = [...stateTools, ...buildSkillDocTools()];
|
|
9136
|
-
this.scopes.set(projectName,
|
|
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) ??
|
|
9210
|
-
|
|
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:
|
|
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
|
|
9861
|
+
const body = request.body;
|
|
9862
|
+
const promptText = (body?.prompt ?? "").trim();
|
|
9385
9863
|
if (!promptText) throw validationError('"prompt" is required');
|
|
9386
|
-
const requestedScope =
|
|
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:
|
|
9389
|
-
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
|
|
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:
|
|
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:
|
|
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
|
|
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 =
|
|
10457
|
-
const derived =
|
|
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 =
|
|
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:
|
|
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 =
|
|
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 ??
|
|
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_${
|
|
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 =
|
|
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 =
|
|
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:
|
|
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
|
};
|