@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.
- package/README.md +4 -1
- package/assets/assets/{BacklinksPage-v2RC8wlg.js → BacklinksPage-lXt19SLR.js} +1 -1
- package/assets/assets/{ChartPrimitives-B74e5pqn.js → ChartPrimitives-C-sLgrDV.js} +1 -1
- package/assets/assets/ProjectPage-wtvaWIRA.js +7 -0
- package/assets/assets/{RunRow-BmVPjO-m.js → RunRow-Db32k-uH.js} +1 -1
- package/assets/assets/{RunsPage-CK5atXvL.js → RunsPage-DwW3Mely.js} +1 -1
- package/assets/assets/{SettingsPage-DarPFUPl.js → SettingsPage-gXn6OGsu.js} +1 -1
- package/assets/assets/{TrafficPage-Ddnn_WqU.js → TrafficPage-I49ci4gj.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-g08SKnwZ.js → TrafficSourceDetailPage-BCvLztx9.js} +1 -1
- package/assets/assets/{arrow-left-D39Vxasb.js → arrow-left-BpA06YY0.js} +1 -1
- package/assets/assets/{extract-error-message-BiNWqBeh.js → extract-error-message-BZSIQ5iV.js} +1 -1
- package/assets/assets/{index-Dd4J1VhB.js → index-DSSLug80.js} +64 -64
- package/assets/assets/{trash-2-xh6gsnap.js → trash-2-CLkUNdvb.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-IDI6LMYL.js → chunk-2FUODJQR.js} +6 -2
- package/dist/{chunk-ALJSES3X.js → chunk-3PVCILSN.js} +565 -51
- package/dist/{chunk-YBIT5O62.js → chunk-B2H2RRIT.js} +927 -614
- package/dist/{chunk-JF7EXHGA.js → chunk-EOQXB63S.js} +30 -2
- package/dist/cli.js +44 -11
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-TWCEKVKZ.js → intelligence-service-RC4BY5HF.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +10 -10
- 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-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-
|
|
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-
|
|
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://
|
|
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://
|
|
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
|
|
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-
|
|
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
|
|
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
|
-
|
|
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 ??
|
|
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 =
|
|
8974
|
+
const stateTools = buildAeroStateTools(toolCtx, { scope: toolScope, profile: toolProfile });
|
|
8516
8975
|
const defaultTools = [...stateTools, ...buildSkillDocTools()];
|
|
8517
8976
|
const tools = opts.tools ?? defaultTools;
|
|
8518
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 ??
|
|
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 ??
|
|
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
|
|
9053
|
-
* read-only share the same cached Agent; each request
|
|
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.
|
|
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
|
-
|
|
9130
|
-
if (this.scopes.get(projectName) ===
|
|
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 =
|
|
9640
|
+
const stateTools = buildAeroStateTools(toolCtx, want);
|
|
9135
9641
|
agent.state.tools = [...stateTools, ...buildSkillDocTools()];
|
|
9136
|
-
this.scopes.set(projectName,
|
|
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) ??
|
|
9210
|
-
|
|
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:
|
|
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
|
|
9893
|
+
const body = request.body;
|
|
9894
|
+
const promptText = (body?.prompt ?? "").trim();
|
|
9385
9895
|
if (!promptText) throw validationError('"prompt" is required');
|
|
9386
|
-
const requestedScope =
|
|
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:
|
|
9389
|
-
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
|
|
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:
|
|
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:
|
|
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
|
|
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 =
|
|
10457
|
-
const derived =
|
|
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 =
|
|
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:
|
|
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 =
|
|
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 ??
|
|
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_${
|
|
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 =
|
|
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 =
|
|
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:
|
|
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
|
};
|