@canonry/canonry 7.7.0 → 7.9.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/agent-workspace/skills/aero/SKILL.md +43 -17
- package/assets/agent-workspace/skills/aero/references/agent-operations.md +4 -1
- package/assets/agent-workspace/skills/aero/references/portfolio-analysis.md +15 -7
- package/assets/agent-workspace/skills/canonry/SKILL.md +4 -1
- package/assets/assets/{AuditHistoryPanel-qumC-I56.js → AuditHistoryPanel-D0nukTwD.js} +1 -1
- package/assets/assets/{BacklinksPage-BNyYcY5w.js → BacklinksPage-B525ij0g.js} +1 -1
- package/assets/assets/{HistoryPage-CQfOM6NA.js → HistoryPage-DvupqQDM.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-D0LTQHL7.js → MeasurementPropertyPage-CwDbi1XN.js} +1 -1
- package/assets/assets/{ProjectPage-QQErJAT8.js → ProjectPage-DmZZ2C7q.js} +1 -1
- package/assets/assets/{RunsPage-CniAGDVF.js → RunsPage-DHTvHcGm.js} +1 -1
- package/assets/assets/{SettingsPage-BPsrE4ao.js → SettingsPage-BwAFzB7A.js} +1 -1
- package/assets/assets/SiteHealthSection-hi54eSIt.js +4 -0
- package/assets/assets/{TrafficPage-DXKk92zy.js → TrafficPage-BOcXASAM.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-4POLIA_c.js → TrafficSourceDetailPage-DTq0cN6g.js} +1 -1
- package/assets/assets/{extract-error-message-C6t0hB2w.js → extract-error-message-BOTDI6g5.js} +1 -1
- package/assets/assets/index-BLJfGYUw.js +86 -0
- package/assets/assets/{index-HWSQgVum.css → index-Rcbm8bK1.css} +1 -1
- package/assets/assets/{react-sigma_core.esm.min-D7GjzWYV.js → react-sigma_core.esm.min-BEBdJNtD.js} +1 -1
- package/assets/assets/{v2-overview-adapter-ChE5k_dV.js → v2-overview-adapter-BPZ-H3VY.js} +1 -1
- package/assets/index.html +2 -2
- package/dist/{chunk-ODEAE5T5.js → chunk-AAMKTL6B.js} +3 -3
- package/dist/{chunk-WJJTKZKK.js → chunk-DO7XPDG5.js} +5977 -5743
- package/dist/{chunk-75RSUOHZ.js → chunk-KRFTA4I7.js} +1126 -475
- package/dist/{chunk-WBTYXLXL.js → chunk-LO56QXPO.js} +5 -4
- package/dist/{chunk-LFUSODDL.js → chunk-NU62UT2S.js} +444 -158
- package/dist/{chunk-JJXTHJ4E.js → chunk-T2V2PESH.js} +4 -3
- package/dist/{chunk-3DB43KT2.js → chunk-ZLB7WX5D.js} +26 -33
- package/dist/cli.js +67 -28
- package/dist/{demo-server-UWBPY743.js → demo-server-KDMG3APY.js} +4 -4
- package/dist/index.js +6 -6
- package/dist/{intelligence-service-LOH37GPJ.js → intelligence-service-LAXJFE3U.js} +2 -2
- package/dist/mcp.js +4 -4
- package/package.json +9 -9
- package/assets/assets/SiteHealthSection-zW3ykBCu.js +0 -4
- package/assets/assets/index-DBQ-8jB1.js +0 -86
|
@@ -20,20 +20,20 @@ import {
|
|
|
20
20
|
saveConfig,
|
|
21
21
|
saveConfigPatch,
|
|
22
22
|
sentimentInstallReadiness
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-LO56QXPO.js";
|
|
24
24
|
import {
|
|
25
25
|
computeFactorAverages,
|
|
26
26
|
deleteSiteCrawlGraphLayout,
|
|
27
27
|
persistSiteCrawlGraphLayout,
|
|
28
28
|
prepareSiteCrawlGraphLayout,
|
|
29
29
|
recordedSharePct
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-T2V2PESH.js";
|
|
31
31
|
import {
|
|
32
32
|
CanonryMcpToolNames,
|
|
33
33
|
CliError,
|
|
34
34
|
PACKAGE_VERSION,
|
|
35
35
|
canonryMcpTools
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-ZLB7WX5D.js";
|
|
37
37
|
import {
|
|
38
38
|
CC_CACHE_DIR,
|
|
39
39
|
DUCKDB_SPEC,
|
|
@@ -205,7 +205,7 @@ import {
|
|
|
205
205
|
siteLivenessState,
|
|
206
206
|
toAlertView,
|
|
207
207
|
usageCounters
|
|
208
|
-
} from "./chunk-
|
|
208
|
+
} from "./chunk-KRFTA4I7.js";
|
|
209
209
|
import {
|
|
210
210
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
211
211
|
AGENT_NONE,
|
|
@@ -238,6 +238,7 @@ import {
|
|
|
238
238
|
LlmCapabilities,
|
|
239
239
|
MEASUREMENT_CHANGES_NOISE_ANSWERS,
|
|
240
240
|
MEASUREMENT_PLAN_V2_SCHEMA_VERSION,
|
|
241
|
+
MeasurementPortfolioLists,
|
|
241
242
|
MemorySources,
|
|
242
243
|
ONBOARDING_FLOW_VERSION,
|
|
243
244
|
OUTSTANDING_PROVIDER_BATCH_STATUSES,
|
|
@@ -403,6 +404,7 @@ import {
|
|
|
403
404
|
templateLinkUbiquityAvailable,
|
|
404
405
|
textContainsBrandAlias,
|
|
405
406
|
textContainsDomain,
|
|
407
|
+
truncateUtf16,
|
|
406
408
|
updateCheckEnvOptOut,
|
|
407
409
|
upgradeCaveatFor,
|
|
408
410
|
upgradeCommandFor,
|
|
@@ -412,7 +414,7 @@ import {
|
|
|
412
414
|
visibilityReportRequestSchema,
|
|
413
415
|
winnabilityClassLabel,
|
|
414
416
|
withRetry
|
|
415
|
-
} from "./chunk-
|
|
417
|
+
} from "./chunk-DO7XPDG5.js";
|
|
416
418
|
|
|
417
419
|
// src/runtime-context.ts
|
|
418
420
|
function cliRuntimeContext(env = process.env, stdio = process) {
|
|
@@ -7870,7 +7872,7 @@ function nonBlank(value) {
|
|
|
7870
7872
|
return trimmed ? trimmed : void 0;
|
|
7871
7873
|
}
|
|
7872
7874
|
function resolveBuildCommit(env = process.env) {
|
|
7873
|
-
const embedded = true ? "
|
|
7875
|
+
const embedded = true ? "c54a2244dca1abcb4657e792eaa6f89bb0d230d5" : void 0;
|
|
7874
7876
|
return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
|
|
7875
7877
|
}
|
|
7876
7878
|
function resolveInstanceIdentity(env = process.env) {
|
|
@@ -16262,7 +16264,7 @@ import { Type as Type3 } from "@earendil-works/pi-ai";
|
|
|
16262
16264
|
|
|
16263
16265
|
// src/agent/mcp-to-agent-tool.ts
|
|
16264
16266
|
import { Type as Type2 } from "@earendil-works/pi-ai";
|
|
16265
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
16267
|
+
import { createHash as createHash5, randomUUID as randomUUID2 } from "crypto";
|
|
16266
16268
|
|
|
16267
16269
|
// src/agent/tool-result-units.ts
|
|
16268
16270
|
var SUCCESS_STATUSES = ["200", "201", "202"];
|
|
@@ -16353,16 +16355,25 @@ function renderRatioUnits(schema, value, components = {}) {
|
|
|
16353
16355
|
|
|
16354
16356
|
// src/agent/mcp-to-agent-tool.ts
|
|
16355
16357
|
var MAX_TOOL_RESULT_CHARS = 2e4;
|
|
16356
|
-
var TRUNCATION_NOTE = "... (truncated, result too large)";
|
|
16357
16358
|
var TRUNCATION_SUMMARY_KEY = "__truncation";
|
|
16358
16359
|
var PARTIAL_LISTS_KEY = "__partialLists";
|
|
16359
16360
|
var CURSOR_KEYS = ["nextCursor", "nextOffset", "nextPageToken", "next_cursor"];
|
|
16361
|
+
var PORTFOLIO_PAGE_LISTS = /* @__PURE__ */ new Map([
|
|
16362
|
+
[MeasurementPortfolioLists["weakest-properties"], { path: "weakestProperties", totalKey: "totalProperties" }],
|
|
16363
|
+
[MeasurementPortfolioLists["strongest-mentions"], { path: "mentionRanking.strongest", totalKey: "eligiblePropertyCount" }],
|
|
16364
|
+
[MeasurementPortfolioLists["weakest-mentions"], { path: "mentionRanking.weakest", totalKey: "eligiblePropertyCount" }],
|
|
16365
|
+
[MeasurementPortfolioLists["excluded-mentions"], { path: "mentionRanking.excluded", totalKey: "excludedTotal" }],
|
|
16366
|
+
[MeasurementPortfolioLists.markets, { path: "markets", totalKey: "totalMarkets" }],
|
|
16367
|
+
[MeasurementPortfolioLists["observed-names"], { path: "answerEvidence.observedNames", totalKey: "observedNamesTotal" }],
|
|
16368
|
+
[MeasurementPortfolioLists["cited-domains"], { path: "answerEvidence.citedDomains", totalKey: "citedDomainsTotal" }]
|
|
16369
|
+
]);
|
|
16360
16370
|
var MAX_STRUCTURED_TRUNCATION_CHARS = 2e6;
|
|
16361
16371
|
var TRUNCATION_SERIALIZE_BUDGET_CHARS = 64e6;
|
|
16362
16372
|
var SMALL_LIST_ROWS = 25;
|
|
16363
|
-
var MAX_SUMMARY_DEPTH = 5;
|
|
16364
16373
|
var MAX_SUMMARY_KEYS = 30;
|
|
16365
|
-
var
|
|
16374
|
+
var MAX_SUMMARY_PATH_CHARS = 96;
|
|
16375
|
+
var PROJECTION_SUMMARY_CHARS = 5e3;
|
|
16376
|
+
var PROJECTION_METADATA_CHARS = PROJECTION_SUMMARY_CHARS - 500;
|
|
16366
16377
|
var MAX_PARTIAL_DEPTH = 4;
|
|
16367
16378
|
var MAX_PARTIAL_NOTES = 12;
|
|
16368
16379
|
function serializeResult(value) {
|
|
@@ -16381,10 +16392,47 @@ function keptSummary(counts, cursors = {}) {
|
|
|
16381
16392
|
if (Object.keys(cursors).length > 0) summary.cursors = cursors;
|
|
16382
16393
|
return summary;
|
|
16383
16394
|
}
|
|
16384
|
-
function noteCursor(owner, ownerPath, listPath, kept, total, notes) {
|
|
16385
|
-
const
|
|
16386
|
-
if (!
|
|
16387
|
-
notes[
|
|
16395
|
+
function noteCursor(owner, ownerPath, listPath, kept, total, notes, root = owner) {
|
|
16396
|
+
const cursor = cursorForList(owner, ownerPath, listPath.slice(listPath.lastIndexOf(".") + 1), root);
|
|
16397
|
+
if (!cursor || kept >= total) return;
|
|
16398
|
+
notes[cursor.path] = `incomplete page: skips the ${total - kept} rows cut from ${listPath}; re-request the original cursor with ${kept > 0 ? `${cursor.limit} <= ${kept}` : `a lower ${cursor.limit}`}`;
|
|
16399
|
+
}
|
|
16400
|
+
function cursorForList(owner, ownerPath, listKey, root = owner) {
|
|
16401
|
+
const prefix = ownerPath ? `${ownerPath}.` : "";
|
|
16402
|
+
if (listKey === "queries" && isRecord5(owner.queryPage) && owner.queryPage.nextCursor !== void 0 && owner.queryPage.nextCursor !== null) {
|
|
16403
|
+
const page = owner.queryPage;
|
|
16404
|
+
return {
|
|
16405
|
+
path: `${prefix}queryPage.nextCursor`,
|
|
16406
|
+
value: page.nextCursor,
|
|
16407
|
+
parameter: "queryCursor",
|
|
16408
|
+
limit: "queryLimit",
|
|
16409
|
+
...typeof page.total === "number" ? { total: { count: page.total, text: String(page.total) } } : {}
|
|
16410
|
+
};
|
|
16411
|
+
}
|
|
16412
|
+
if (listKey === "attempts" && owner.nextAttemptCursor !== void 0 && owner.nextAttemptCursor !== null) {
|
|
16413
|
+
return {
|
|
16414
|
+
path: `${prefix}nextAttemptCursor`,
|
|
16415
|
+
value: owner.nextAttemptCursor,
|
|
16416
|
+
parameter: "attemptCursor",
|
|
16417
|
+
limit: "attemptLimit",
|
|
16418
|
+
...typeof owner.attemptCount === "number" ? { total: { count: owner.attemptCount, text: String(owner.attemptCount) } } : {}
|
|
16419
|
+
};
|
|
16420
|
+
}
|
|
16421
|
+
const selected = typeof root.pageList === "string" ? PORTFOLIO_PAGE_LISTS.get(root.pageList) : void 0;
|
|
16422
|
+
const selectedList = selected?.path === `${prefix}${listKey}`;
|
|
16423
|
+
if (selected && owner === root && !selectedList) return void 0;
|
|
16424
|
+
const cursorOwner = selectedList ? root : owner;
|
|
16425
|
+
const cursorPrefix = selectedList ? "" : prefix;
|
|
16426
|
+
const key = CURSOR_KEYS.find((candidate) => cursorOwner[candidate] !== void 0 && cursorOwner[candidate] !== null);
|
|
16427
|
+
if (!key) return void 0;
|
|
16428
|
+
const total = selectedList ? owner[selected.totalKey] : void 0;
|
|
16429
|
+
return {
|
|
16430
|
+
path: `${cursorPrefix}${key}`,
|
|
16431
|
+
value: cursorOwner[key],
|
|
16432
|
+
parameter: "cursor",
|
|
16433
|
+
limit: "limit",
|
|
16434
|
+
...typeof total === "number" ? { total: { count: total, text: String(total) } } : {}
|
|
16435
|
+
};
|
|
16388
16436
|
}
|
|
16389
16437
|
function ownListTotal(numbers, key) {
|
|
16390
16438
|
const lower = key.toLowerCase();
|
|
@@ -16412,9 +16460,14 @@ function partialLists(root) {
|
|
|
16412
16460
|
}
|
|
16413
16461
|
let noted = false;
|
|
16414
16462
|
if (lists.length <= MAX_SUMMARY_KEYS) {
|
|
16415
|
-
const counts = lists.map(([key]) => ownListTotal(numbers, key));
|
|
16463
|
+
const counts = lists.map(([key]) => ownListTotal(numbers, key) ?? cursorForList(object, path9, key, root)?.total);
|
|
16416
16464
|
const uncounted = counts.flatMap((count3, index) => count3 ? [] : [index]);
|
|
16417
|
-
if (uncounted.length === 1)
|
|
16465
|
+
if (uncounted.length === 1) {
|
|
16466
|
+
const index = uncounted[0];
|
|
16467
|
+
const key = lists[index][0];
|
|
16468
|
+
const paged = cursorForList(object, path9, key, root) || typeof object.truncated === "boolean" || typeof object[`${key}Truncated`] === "boolean" || ["items", "rows", "results"].includes(key);
|
|
16469
|
+
if (paged) counts[index] = bareTotal(numbers);
|
|
16470
|
+
}
|
|
16418
16471
|
for (const [index, [key, rows]] of lists.entries()) {
|
|
16419
16472
|
const label = path9 ? `${path9}.${key}` : key;
|
|
16420
16473
|
const count3 = counts[index];
|
|
@@ -16442,9 +16495,25 @@ function withPartialLists(value) {
|
|
|
16442
16495
|
if (!isRecord5(value) || typeof value.toJSON === "function") return value;
|
|
16443
16496
|
const hadNote = Object.hasOwn(value, PARTIAL_LISTS_KEY);
|
|
16444
16497
|
const notes = partialLists(value);
|
|
16445
|
-
if (!notes && !hadNote) return value;
|
|
16446
|
-
const
|
|
16447
|
-
|
|
16498
|
+
if (!notes && !hadNote && value.nextAttemptCursor == null && !CURSOR_KEYS.some((key) => value[key] !== void 0 && value[key] !== null) && !Object.values(value).some((child) => isRecord5(child) && CURSOR_KEYS.some((key) => child[key] !== void 0 && child[key] !== null))) return value;
|
|
16499
|
+
const pagination = {};
|
|
16500
|
+
const visit = (owner, path9, depth) => {
|
|
16501
|
+
const lists = Object.entries(owner).filter((entry) => Array.isArray(entry[1]));
|
|
16502
|
+
const numbers = Object.entries(owner).filter((entry) => typeof entry[1] === "number");
|
|
16503
|
+
for (const [key, rows] of lists) {
|
|
16504
|
+
const cursor = cursorForList(owner, path9, key, value);
|
|
16505
|
+
if (!cursor || key.startsWith("__") || Object.keys(pagination).length >= MAX_PARTIAL_NOTES) continue;
|
|
16506
|
+
const label = path9 ? `${path9}.${key}` : key;
|
|
16507
|
+
const total = cursor.total ?? ownListTotal(numbers, key) ?? (lists.length === 1 ? bareTotal(numbers) : void 0);
|
|
16508
|
+
const incomplete = owner.__truncated === true || isRecord5(owner.__omittedRowsByField) && typeof owner.__omittedRowsByField[key] === "number";
|
|
16509
|
+
pagination[label] = `showing ${rows.length}${total ? ` of ${total.text}` : " rows"}; ${incomplete ? `incomplete page: re-request the original cursor with a smaller ${cursor.limit}; the next cursor skips omitted rows` : `pass ${cursor.parameter} ${JSON.stringify(cursor.value)}`}`;
|
|
16510
|
+
}
|
|
16511
|
+
if (depth >= MAX_PARTIAL_DEPTH) return;
|
|
16512
|
+
for (const [key, child] of Object.entries(owner)) if (!key.startsWith("__") && isRecord5(child)) visit(child, path9 ? `${path9}.${key}` : key, depth + 1);
|
|
16513
|
+
};
|
|
16514
|
+
visit(value, "", 1);
|
|
16515
|
+
const { [PARTIAL_LISTS_KEY]: _previous, __pagination: _pagination, ...rest } = value;
|
|
16516
|
+
return { ...notes ? { [PARTIAL_LISTS_KEY]: notes } : {}, ...Object.keys(pagination).length > 0 ? { __pagination: pagination } : {}, ...rest };
|
|
16448
16517
|
}
|
|
16449
16518
|
function largestArrayKey(obj) {
|
|
16450
16519
|
let best;
|
|
@@ -16569,7 +16638,7 @@ function currentSummary(root, owned) {
|
|
|
16569
16638
|
count3.kept += value.length;
|
|
16570
16639
|
count3.total += list.rows.length;
|
|
16571
16640
|
counts.set(list.path, count3);
|
|
16572
|
-
noteCursor(object, path9, list.path, value.length, list.rows.length, cursors);
|
|
16641
|
+
noteCursor(object, path9, list.path, value.length, list.rows.length, cursors, root);
|
|
16573
16642
|
}
|
|
16574
16643
|
for (const row of value) if (isRecord5(row)) visit(row, `${valuePath}[]`);
|
|
16575
16644
|
} else if (isRecord5(value)) {
|
|
@@ -16672,123 +16741,107 @@ function hasOtherLists(value, primary) {
|
|
|
16672
16741
|
}
|
|
16673
16742
|
return isRecord5(value) && Object.values(value).some((child) => hasOtherLists(child, primary));
|
|
16674
16743
|
}
|
|
16675
|
-
function
|
|
16676
|
-
|
|
16677
|
-
|
|
16678
|
-
|
|
16679
|
-
if (container.kind === "array") return `${container.path}[${container.complete}]`;
|
|
16680
|
-
const key = shortSegment(container.key ?? "");
|
|
16681
|
-
return container.path ? `${container.path}.${key}` : key;
|
|
16682
|
-
}
|
|
16683
|
-
function serializedKeys(value) {
|
|
16684
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
16685
|
-
const record2 = value;
|
|
16686
|
-
if (typeof record2.toJSON === "function") return void 0;
|
|
16687
|
-
return Object.keys(record2).filter((key) => {
|
|
16688
|
-
const child = record2[key];
|
|
16689
|
-
return child !== void 0 && typeof child !== "function" && typeof child !== "symbol";
|
|
16690
|
-
});
|
|
16744
|
+
function projectionPath(path9) {
|
|
16745
|
+
if (path9.length <= MAX_SUMMARY_PATH_CHARS) return path9;
|
|
16746
|
+
const digest = createHash5("sha256").update(path9).digest("hex").slice(0, 12);
|
|
16747
|
+
return `${truncateUtf16(path9, 48)}...#${digest}${path9.slice(-24)}`;
|
|
16691
16748
|
}
|
|
16692
|
-
function
|
|
16693
|
-
const
|
|
16694
|
-
|
|
16695
|
-
|
|
16696
|
-
|
|
16697
|
-
|
|
16698
|
-
|
|
16699
|
-
|
|
16700
|
-
|
|
16701
|
-
|
|
16702
|
-
|
|
16703
|
-
|
|
16704
|
-
|
|
16705
|
-
|
|
16706
|
-
|
|
16707
|
-
|
|
16749
|
+
function boundedProjection(full) {
|
|
16750
|
+
const value = JSON.parse(full);
|
|
16751
|
+
const summary = {
|
|
16752
|
+
droppedKeys: [],
|
|
16753
|
+
keptItems: {},
|
|
16754
|
+
projection: "Only complete array rows and marked partial string prefixes are shown; omitted fields are not evidence. Never follow a cursor from a cut page; re-request the original cursor with a smaller limit."
|
|
16755
|
+
};
|
|
16756
|
+
const skippedMetadata = () => {
|
|
16757
|
+
summary.moreMetadataEntries = (summary.moreMetadataEntries ?? 0) + 1;
|
|
16758
|
+
};
|
|
16759
|
+
const record2 = (entries, path9, value2) => {
|
|
16760
|
+
if (Object.keys(entries).length >= MAX_SUMMARY_KEYS) {
|
|
16761
|
+
skippedMetadata();
|
|
16762
|
+
return false;
|
|
16763
|
+
}
|
|
16764
|
+
const key = projectionPath(path9);
|
|
16765
|
+
entries[key] = value2;
|
|
16766
|
+
if (serializeResult(summary).length <= PROJECTION_METADATA_CHARS) return true;
|
|
16767
|
+
delete entries[key];
|
|
16768
|
+
skippedMetadata();
|
|
16769
|
+
return false;
|
|
16770
|
+
};
|
|
16771
|
+
let dropped = 0;
|
|
16772
|
+
const omit = (path9) => {
|
|
16773
|
+
dropped++;
|
|
16774
|
+
if (summary.droppedKeys.length < MAX_SUMMARY_KEYS) {
|
|
16775
|
+
summary.droppedKeys.push(projectionPath(path9));
|
|
16776
|
+
if (serializeResult(summary).length > PROJECTION_METADATA_CHARS) summary.droppedKeys.pop();
|
|
16777
|
+
}
|
|
16778
|
+
if (dropped > summary.droppedKeys.length) summary.moreDroppedKeys = dropped - summary.droppedKeys.length;
|
|
16779
|
+
};
|
|
16780
|
+
const project = (input, budget, path9, owner, ownerPath = "") => {
|
|
16781
|
+
if (Array.isArray(input)) {
|
|
16782
|
+
const rows = [];
|
|
16783
|
+
let used2 = 2;
|
|
16784
|
+
for (const row of input) {
|
|
16785
|
+
const text = serializeResult(row);
|
|
16786
|
+
const cost = text.length + (rows.length > 0 ? 1 : 0);
|
|
16787
|
+
if (used2 + cost > budget) break;
|
|
16788
|
+
rows.push(text);
|
|
16789
|
+
used2 += cost;
|
|
16790
|
+
}
|
|
16791
|
+
if (rows.length < input.length) {
|
|
16792
|
+
record2(summary.keptItems, path9 || "items", `${rows.length} of ${input.length}`);
|
|
16793
|
+
if (rows.length === 0) omit(path9 || "items");
|
|
16794
|
+
if (owner) {
|
|
16795
|
+
const cursors = {};
|
|
16796
|
+
noteCursor(owner, ownerPath, path9, rows.length, input.length, cursors, isRecord5(value) ? value : owner);
|
|
16797
|
+
for (const [cursor, note] of Object.entries(cursors)) {
|
|
16798
|
+
record2(summary.cursors ??= {}, cursor, note.replace(path9, projectionPath(path9)));
|
|
16799
|
+
}
|
|
16708
16800
|
}
|
|
16709
16801
|
}
|
|
16710
|
-
|
|
16802
|
+
return `[${rows.join(",")}]`;
|
|
16711
16803
|
}
|
|
16712
|
-
if (
|
|
16713
|
-
|
|
16714
|
-
|
|
16715
|
-
|
|
16716
|
-
|
|
16717
|
-
|
|
16718
|
-
|
|
16719
|
-
|
|
16720
|
-
|
|
16721
|
-
|
|
16722
|
-
|
|
16723
|
-
|
|
16724
|
-
|
|
16725
|
-
|
|
16726
|
-
|
|
16727
|
-
|
|
16728
|
-
|
|
16729
|
-
|
|
16730
|
-
|
|
16731
|
-
const
|
|
16732
|
-
if (
|
|
16733
|
-
|
|
16734
|
-
|
|
16735
|
-
|
|
16736
|
-
|
|
16737
|
-
|
|
16738
|
-
|
|
16739
|
-
|
|
16740
|
-
}
|
|
16741
|
-
|
|
16742
|
-
if (!shown.startsWith("{") && !shown.startsWith("[")) return "";
|
|
16743
|
-
const stack = openContainersAt(shown, details);
|
|
16744
|
-
const root = stack.at(0);
|
|
16745
|
-
const deepest = stack.at(-1);
|
|
16746
|
-
if (!root || !deepest) return "";
|
|
16747
|
-
const summary = {
|
|
16748
|
-
// Between two children the cut is named by their container.
|
|
16749
|
-
cutAt: deepest.childClosed || deepest.expectKey ? deepest.path : childPath(deepest),
|
|
16750
|
-
droppedKeys: [],
|
|
16751
|
-
keptItems: {}
|
|
16804
|
+
if (!isRecord5(input)) {
|
|
16805
|
+
const text = serializeResult(input);
|
|
16806
|
+
if (text.length <= budget) return text;
|
|
16807
|
+
if (typeof input === "string" && budget > 2) {
|
|
16808
|
+
const kept = largestFitting(Math.min(input.length, budget - 2), (length) => serializeResult(truncateUtf16(input, length)).length <= budget);
|
|
16809
|
+
const prefix = truncateUtf16(input, kept);
|
|
16810
|
+
if (prefix.length > 0 && record2(summary.slicedKeys ??= {}, path9 || "(root)", { keptChars: prefix.length, totalChars: input.length })) return serializeResult(prefix);
|
|
16811
|
+
}
|
|
16812
|
+
omit(path9 || "(root)");
|
|
16813
|
+
return void 0;
|
|
16814
|
+
}
|
|
16815
|
+
const fields = Object.entries(input).filter(([key]) => !key.startsWith("__"));
|
|
16816
|
+
const priority = ([key, child]) => key === "queryPage" && isRecord5(child) ? 0 : typeof child === "string" && child.length > budget ? 2 : Number(typeof child === "object" && child !== null);
|
|
16817
|
+
fields.sort((a, b) => priority(a) - priority(b));
|
|
16818
|
+
const shown2 = [];
|
|
16819
|
+
let used = 2;
|
|
16820
|
+
for (const [key, child] of fields) {
|
|
16821
|
+
const childPath = path9 ? `${path9}.${key}` : key;
|
|
16822
|
+
const prefix = `${JSON.stringify(key)}:`;
|
|
16823
|
+
const allowance = budget - used - prefix.length - (shown2.length > 0 ? 1 : 0);
|
|
16824
|
+
if (allowance < 2) {
|
|
16825
|
+
omit(childPath);
|
|
16826
|
+
continue;
|
|
16827
|
+
}
|
|
16828
|
+
const text = project(child, allowance, childPath, input, path9);
|
|
16829
|
+
if (text === void 0) continue;
|
|
16830
|
+
shown2.push(prefix + text);
|
|
16831
|
+
used += prefix.length + text.length + (shown2.length > 1 ? 1 : 0);
|
|
16832
|
+
}
|
|
16833
|
+
return `{${shown2.join(",")}}`;
|
|
16752
16834
|
};
|
|
16753
|
-
|
|
16754
|
-
|
|
16755
|
-
|
|
16756
|
-
|
|
16757
|
-
const shownCount = container.complete + (container.childClosed ? 1 : 0);
|
|
16758
|
-
summary.keptItems[container.path || "(root)"] = container.kind === "array" ? `${shownCount} of ${total}` : `${shownCount} of ${total} keys`;
|
|
16759
|
-
}
|
|
16760
|
-
const rootKeys = root.kind === "object" ? serializedKeys(root.value) : void 0;
|
|
16761
|
-
if (rootKeys) {
|
|
16762
|
-
const inProgress = root.expectKey || root.childClosed ? 0 : 1;
|
|
16763
|
-
const dropped = rootKeys.slice(root.complete + (root.childClosed ? 1 : 0) + inProgress);
|
|
16764
|
-
const record2 = root.value;
|
|
16765
|
-
for (const key of dropped.slice(0, MAX_SUMMARY_KEYS)) {
|
|
16766
|
-
const label = shortSegment(key);
|
|
16767
|
-
summary.droppedKeys.push(label);
|
|
16768
|
-
const value = record2[key];
|
|
16769
|
-
if (Array.isArray(value)) summary.keptItems[label] = `0 of ${value.length}`;
|
|
16770
|
-
}
|
|
16771
|
-
if (dropped.length > MAX_SUMMARY_KEYS) summary.moreDroppedKeys = dropped.length - MAX_SUMMARY_KEYS;
|
|
16772
|
-
}
|
|
16773
|
-
return `${TRUNCATION_SUMMARY_KEY}: ${JSON.stringify(summary)}`;
|
|
16774
|
-
}
|
|
16775
|
-
function markedSlice(full, details) {
|
|
16776
|
-
let cut = MAX_TOOL_RESULT_CHARS;
|
|
16777
|
-
for (let attempt = 0; attempt < 4; attempt++) {
|
|
16778
|
-
const line = sliceSummaryLine(full.slice(0, cut), details);
|
|
16779
|
-
const cost = line ? line.length + 1 : 0;
|
|
16780
|
-
if (cut + cost <= MAX_TOOL_RESULT_CHARS) {
|
|
16781
|
-
return full.slice(0, cut) + "\n" + (line ? line + "\n" : "") + TRUNCATION_NOTE;
|
|
16782
|
-
}
|
|
16783
|
-
cut = MAX_TOOL_RESULT_CHARS - cost - attempt * 16;
|
|
16784
|
-
}
|
|
16785
|
-
return full.slice(0, MAX_TOOL_RESULT_CHARS) + "\n" + TRUNCATION_NOTE;
|
|
16835
|
+
const projected = project(value, MAX_TOOL_RESULT_CHARS - PROJECTION_SUMMARY_CHARS, "");
|
|
16836
|
+
const shown = projected === void 0 ? {} : JSON.parse(projected);
|
|
16837
|
+
const output = isRecord5(shown) ? shown : { items: shown };
|
|
16838
|
+
return serializeResult({ ...output, __truncated: true, [TRUNCATION_SUMMARY_KEY]: summary });
|
|
16786
16839
|
}
|
|
16787
16840
|
function truncateToolResult(details) {
|
|
16788
16841
|
const annotated = withPartialLists(details);
|
|
16789
16842
|
const full = serializeResult(annotated);
|
|
16790
16843
|
if (full.length <= MAX_TOOL_RESULT_CHARS) return full;
|
|
16791
|
-
if (full.length > MAX_STRUCTURED_TRUNCATION_CHARS) return
|
|
16844
|
+
if (full.length > MAX_STRUCTURED_TRUNCATION_CHARS) return boundedProjection(full);
|
|
16792
16845
|
if (Array.isArray(details)) {
|
|
16793
16846
|
const render2 = (rows) => serializeResult({
|
|
16794
16847
|
items: rows,
|
|
@@ -16801,7 +16854,7 @@ function truncateToolResult(details) {
|
|
|
16801
16854
|
if (details && typeof details === "object") {
|
|
16802
16855
|
const obj = details;
|
|
16803
16856
|
const arrayKey = largestArrayKey(obj);
|
|
16804
|
-
if (!arrayKey) return trimNestedArrays(full) ??
|
|
16857
|
+
if (!arrayKey) return trimNestedArrays(full) ?? boundedProjection(full);
|
|
16805
16858
|
const rows = obj[arrayKey];
|
|
16806
16859
|
const render2 = (kept) => {
|
|
16807
16860
|
const cursors = {};
|
|
@@ -16819,7 +16872,7 @@ function truncateToolResult(details) {
|
|
|
16819
16872
|
if (fitsCap(out) && (nested === void 0 || keptRowTotal(nested) <= keptRowTotal(out))) return out;
|
|
16820
16873
|
if (nested !== void 0) return nested;
|
|
16821
16874
|
}
|
|
16822
|
-
return
|
|
16875
|
+
return boundedProjection(full);
|
|
16823
16876
|
}
|
|
16824
16877
|
function textResult2(details, responseSchemas = []) {
|
|
16825
16878
|
const components = responseSchemas.length > 0 ? responseComponents() : {};
|
|
@@ -16829,6 +16882,40 @@ function textResult2(details, responseSchemas = []) {
|
|
|
16829
16882
|
details
|
|
16830
16883
|
};
|
|
16831
16884
|
}
|
|
16885
|
+
var MEMO_STORED_READS = /* @__PURE__ */ new Set([
|
|
16886
|
+
CanonryMcpToolNames.canonry_competitor_landscape,
|
|
16887
|
+
CanonryMcpToolNames.canonry_measurement_overview,
|
|
16888
|
+
CanonryMcpToolNames.canonry_measurement_portfolio_summary,
|
|
16889
|
+
CanonryMcpToolNames.canonry_measurement_property_evidence,
|
|
16890
|
+
CanonryMcpToolNames.canonry_measurement_property_questions,
|
|
16891
|
+
CanonryMcpToolNames.canonry_measurement_question_result,
|
|
16892
|
+
CanonryMcpToolNames.canonry_measurement_property_competitors,
|
|
16893
|
+
CanonryMcpToolNames.canonry_measurement_changes,
|
|
16894
|
+
CanonryMcpToolNames.canonry_visibility_report,
|
|
16895
|
+
CanonryMcpToolNames.canonry_visibility_stats,
|
|
16896
|
+
CanonryMcpToolNames.canonry_visibility_compare,
|
|
16897
|
+
CanonryMcpToolNames.canonry_sentiment,
|
|
16898
|
+
CanonryMcpToolNames.canonry_sentiment_settings,
|
|
16899
|
+
CanonryMcpToolNames.canonry_sentiment_evidence,
|
|
16900
|
+
CanonryMcpToolNames.canonry_sentiment_compare,
|
|
16901
|
+
CanonryMcpToolNames.canonry_sentiment_backfill_preview,
|
|
16902
|
+
CanonryMcpToolNames.canonry_insights_list
|
|
16903
|
+
]);
|
|
16904
|
+
var STORED_PAGE_READS = /* @__PURE__ */ new Set([
|
|
16905
|
+
...MEMO_STORED_READS,
|
|
16906
|
+
CanonryMcpToolNames.canonry_project_overview,
|
|
16907
|
+
CanonryMcpToolNames.canonry_measurement_data_quality,
|
|
16908
|
+
CanonryMcpToolNames.canonry_sentiment_job,
|
|
16909
|
+
CanonryMcpToolNames.canonry_sentiment_jobs
|
|
16910
|
+
]);
|
|
16911
|
+
var storedReadTools = /* @__PURE__ */ new WeakSet();
|
|
16912
|
+
var storedPageReadTools = /* @__PURE__ */ new WeakSet();
|
|
16913
|
+
function isStoredReadTool(tool) {
|
|
16914
|
+
return storedReadTools.has(tool);
|
|
16915
|
+
}
|
|
16916
|
+
function isStoredPageReadTool(tool) {
|
|
16917
|
+
return storedPageReadTools.has(tool);
|
|
16918
|
+
}
|
|
16832
16919
|
function stripProjectFromJsonSchema(jsonSchema) {
|
|
16833
16920
|
if (!jsonSchema || typeof jsonSchema !== "object") {
|
|
16834
16921
|
return { schema: jsonSchema, hadProject: false };
|
|
@@ -16857,17 +16944,20 @@ function mcpToAgentTool(tool, ctx) {
|
|
|
16857
16944
|
const result2 = await runWithUsageTags(ctx.client, { mcpTool: tool.name, mcpCall: randomUUID2() }, () => tool.handler(ctx.client, handlerInput));
|
|
16858
16945
|
return textResult2(result2, responseSchemas);
|
|
16859
16946
|
};
|
|
16860
|
-
|
|
16947
|
+
const adapted = {
|
|
16861
16948
|
name: tool.name,
|
|
16862
16949
|
label: tool.title,
|
|
16863
16950
|
description: tool.description,
|
|
16864
16951
|
parameters,
|
|
16865
16952
|
execute
|
|
16866
16953
|
};
|
|
16954
|
+
if (tool.access === "read" && MEMO_STORED_READS.has(tool.name)) storedReadTools.add(adapted);
|
|
16955
|
+
if (tool.access === "read" && STORED_PAGE_READS.has(tool.name)) storedPageReadTools.add(adapted);
|
|
16956
|
+
return adapted;
|
|
16867
16957
|
}
|
|
16868
16958
|
var AERO_EXCLUDED_MCP_TOOLS = /* @__PURE__ */ new Set([
|
|
16869
|
-
// Aero reads stored sentiment.
|
|
16870
|
-
// paid classifier work
|
|
16959
|
+
// Aero reads stored sentiment. Configuration and backfill submission can
|
|
16960
|
+
// start paid classifier work and remain operator actions.
|
|
16871
16961
|
"canonry_sentiment_configure",
|
|
16872
16962
|
"canonry_sentiment_backfill",
|
|
16873
16963
|
CanonryMcpToolNames.canonry_agent_clear,
|
|
@@ -17281,6 +17371,8 @@ function splitAeroAnthropicSystemCachePayload(payload, model) {
|
|
|
17281
17371
|
|
|
17282
17372
|
// src/agent/runtime.ts
|
|
17283
17373
|
import { z } from "zod";
|
|
17374
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
17375
|
+
import { isDeepStrictEqual } from "util";
|
|
17284
17376
|
import {
|
|
17285
17377
|
Type as Type4,
|
|
17286
17378
|
createInitialSystemMessage,
|
|
@@ -17291,14 +17383,156 @@ var DISCOVER = "aero_list_toolkits";
|
|
|
17291
17383
|
var MAX_VISIBLE_TOOLS = 128;
|
|
17292
17384
|
var LOAD = "aero_load_toolkit";
|
|
17293
17385
|
var metadata = new Map(canonryMcpTools.map((tool) => [tool.name, tool]));
|
|
17386
|
+
var PAGE_REFERENCE_PREFIX = "aero-page:";
|
|
17387
|
+
var PAGE_SIZE_PARAMS = /* @__PURE__ */ new Set(["limit", "queryLimit", "attemptLimit"]);
|
|
17388
|
+
var PAGE_CURSORS = [
|
|
17389
|
+
{ owner: [], key: "nextCursor", parameter: "cursor" },
|
|
17390
|
+
{ owner: ["properties"], key: "nextCursor", parameter: "cursor" },
|
|
17391
|
+
{ owner: ["queryPage"], key: "nextCursor", parameter: "queryCursor" },
|
|
17392
|
+
{ owner: [], key: "nextAttemptCursor", parameter: "attemptCursor" }
|
|
17393
|
+
];
|
|
17294
17394
|
var runtimes = /* @__PURE__ */ new WeakMap();
|
|
17295
17395
|
var TOOL_LIMIT_WRAP_UP = "This turn has used its tool budget, and no tools are available now. Answer the question from the tool results above: give what they establish with their numbers, say plainly which parts you could not check, and suggest one narrower question the user could ask next.";
|
|
17396
|
+
var TIME_LIMIT_WRAP_UP = TOOL_LIMIT_WRAP_UP.replace("used its tool budget", "used its research time budget");
|
|
17296
17397
|
function result(value) {
|
|
17297
17398
|
return { content: [{ type: "text", text: truncateToolResult(value) }], details: value };
|
|
17298
17399
|
}
|
|
17400
|
+
function pageDefaults(tool, params) {
|
|
17401
|
+
const filled = { ...params };
|
|
17402
|
+
for (const [key, property] of Object.entries(pageOwner(tool.parameters, ["properties"]) ?? {})) {
|
|
17403
|
+
const schema = pageOwner(property, []);
|
|
17404
|
+
if (filled[key] === void 0 && schema && Object.hasOwn(schema, "default")) filled[key] = structuredClone(schema.default);
|
|
17405
|
+
}
|
|
17406
|
+
return filled;
|
|
17407
|
+
}
|
|
17408
|
+
function pageSelection(tool, params, parameter) {
|
|
17409
|
+
const selection = pageDefaults(tool, params);
|
|
17410
|
+
if (tool.name === CanonryMcpToolNames.canonry_measurement_portfolio_summary && selection.list === void 0) {
|
|
17411
|
+
selection.list = MeasurementPortfolioLists["weakest-properties"];
|
|
17412
|
+
}
|
|
17413
|
+
return Object.fromEntries(Object.entries(selection).filter(([key, value]) => key !== parameter && !PAGE_SIZE_PARAMS.has(key) && value !== void 0));
|
|
17414
|
+
}
|
|
17415
|
+
function inheritPageArguments(runtime, message, visible) {
|
|
17416
|
+
for (const block of message.content) {
|
|
17417
|
+
if (block.type !== "toolCall" || !visible.has(block.name)) continue;
|
|
17418
|
+
const tool = runtime.allowed.find((candidate) => candidate.name === block.name);
|
|
17419
|
+
if (!tool || !isStoredPageReadTool(tool) || !pageOwner(block.arguments, [])) continue;
|
|
17420
|
+
let params = block.arguments;
|
|
17421
|
+
for (const parameter of new Set(PAGE_CURSORS.map((cursor) => cursor.parameter))) {
|
|
17422
|
+
const reference = params[parameter];
|
|
17423
|
+
const page = typeof reference === "string" ? runtime.pageReferences.get(reference) : void 0;
|
|
17424
|
+
if (page?.tool === tool && page.parameter === parameter) params = { ...page.params, ...params };
|
|
17425
|
+
}
|
|
17426
|
+
block.arguments = pageDefaults(tool, params);
|
|
17427
|
+
}
|
|
17428
|
+
}
|
|
17429
|
+
function resolvePageReferences(runtime, tool, params) {
|
|
17430
|
+
if (params === null || typeof params !== "object" || Array.isArray(params)) return params;
|
|
17431
|
+
let resolved = params;
|
|
17432
|
+
for (const parameter of new Set(PAGE_CURSORS.map((cursor) => cursor.parameter))) {
|
|
17433
|
+
const value = resolved[parameter];
|
|
17434
|
+
if (typeof value !== "string" || !value.startsWith(PAGE_REFERENCE_PREFIX)) continue;
|
|
17435
|
+
const page = runtime.pageReferences.get(value);
|
|
17436
|
+
if (!page) throw new Error("This Aero page reference is no longer available. Read the first page again with the original filters.");
|
|
17437
|
+
const inherited = { ...page.params, ...resolved };
|
|
17438
|
+
if (page.tool !== tool || page.parameter !== parameter || !isDeepStrictEqual(page.selection, pageSelection(tool, inherited, parameter))) {
|
|
17439
|
+
throw new Error("Use this Aero page reference with the same tool and original filters; only limit, queryLimit or attemptLimit may change.");
|
|
17440
|
+
}
|
|
17441
|
+
resolved = { ...inherited, [parameter]: page.token };
|
|
17442
|
+
}
|
|
17443
|
+
return resolved;
|
|
17444
|
+
}
|
|
17445
|
+
function pageOwner(value, path9) {
|
|
17446
|
+
let owner = value;
|
|
17447
|
+
for (const key of path9) {
|
|
17448
|
+
if (owner === null || typeof owner !== "object" || Array.isArray(owner)) return void 0;
|
|
17449
|
+
owner = owner[key];
|
|
17450
|
+
}
|
|
17451
|
+
return owner !== null && typeof owner === "object" && !Array.isArray(owner) ? owner : void 0;
|
|
17452
|
+
}
|
|
17453
|
+
function referencePages(runtime, tool, params, value) {
|
|
17454
|
+
if (!isStoredPageReadTool(tool) || value.isError || value.content.length !== 1 || params === null || typeof params !== "object" || Array.isArray(params)) return value;
|
|
17455
|
+
const block = value.content[0];
|
|
17456
|
+
if (block.type !== "text") return value;
|
|
17457
|
+
let shown;
|
|
17458
|
+
try {
|
|
17459
|
+
shown = JSON.parse(block.text);
|
|
17460
|
+
} catch {
|
|
17461
|
+
return value;
|
|
17462
|
+
}
|
|
17463
|
+
const root = pageOwner(shown, []);
|
|
17464
|
+
if (!root || root.__truncated === true) return value;
|
|
17465
|
+
let changed = false;
|
|
17466
|
+
for (const cursor of PAGE_CURSORS) {
|
|
17467
|
+
const owner = pageOwner(root, cursor.owner);
|
|
17468
|
+
const token = owner?.[cursor.key];
|
|
17469
|
+
if (!Object.hasOwn(pageOwner(tool.parameters, ["properties"]) ?? {}, cursor.parameter) || !owner || owner.__truncated === true || typeof token !== "string" || token !== pageOwner(value.details, cursor.owner)?.[cursor.key]) continue;
|
|
17470
|
+
if (runtime.pageReferences.size >= runtime.limits.maxToolCalls * PAGE_CURSORS.length) break;
|
|
17471
|
+
const reference = `${PAGE_REFERENCE_PREFIX}${runtime.pageNamespace}-${runtime.pageTurn}:${runtime.pageReferences.size + 1}`;
|
|
17472
|
+
if (token.length <= reference.length) continue;
|
|
17473
|
+
runtime.pageReferences.set(reference, {
|
|
17474
|
+
tool,
|
|
17475
|
+
token,
|
|
17476
|
+
parameter: cursor.parameter,
|
|
17477
|
+
params: structuredClone(params),
|
|
17478
|
+
selection: structuredClone(pageSelection(tool, params, cursor.parameter))
|
|
17479
|
+
});
|
|
17480
|
+
owner[cursor.key] = reference;
|
|
17481
|
+
const pagination = pageOwner(root.__pagination, []);
|
|
17482
|
+
for (const [path9, note] of Object.entries(pagination ?? {})) {
|
|
17483
|
+
if (typeof note === "string") pagination[path9] = note.split(token).join(reference);
|
|
17484
|
+
}
|
|
17485
|
+
changed = true;
|
|
17486
|
+
}
|
|
17487
|
+
if (!changed) return value;
|
|
17488
|
+
root.__pageReferences = "Aero page references expire at the end of this turn. Pass them unchanged to the same tool. Omitted filters inherit the original scope; supplied filters must match. Only the page size may change.";
|
|
17489
|
+
let text = JSON.stringify(root);
|
|
17490
|
+
if (text.length > block.text.length) {
|
|
17491
|
+
delete root.__pageReferences;
|
|
17492
|
+
text = JSON.stringify(root);
|
|
17493
|
+
}
|
|
17494
|
+
return { ...value, content: [{ ...block, text }] };
|
|
17495
|
+
}
|
|
17299
17496
|
function visibleTools(runtime) {
|
|
17300
|
-
|
|
17301
|
-
|
|
17497
|
+
const allowed = runtime.allowed.map((tool) => ({
|
|
17498
|
+
...tool,
|
|
17499
|
+
execute: async (id, params, signal, onUpdate) => {
|
|
17500
|
+
const dispatchedParams = resolvePageReferences(runtime, tool, params);
|
|
17501
|
+
const safeRead = isStoredReadTool(tool);
|
|
17502
|
+
const key = safeRead ? `${tool.name}:${JSON.stringify(dispatchedParams)}` : void 0;
|
|
17503
|
+
const prior = key === void 0 ? void 0 : runtime.memo.get(key);
|
|
17504
|
+
if (prior) {
|
|
17505
|
+
const previous = await prior.value;
|
|
17506
|
+
if (!previous.isError) return result({ alreadyReturned: true, toolCallId: prior.id, note: "Already returned above. Use that result and follow its pagination instruction. A cap-cut page must be re-requested from the original cursor with a smaller limit." });
|
|
17507
|
+
}
|
|
17508
|
+
if (!safeRead) runtime.memo.clear();
|
|
17509
|
+
const research = runtime.research?.signal;
|
|
17510
|
+
const signals = [signal, research].filter((value) => value !== void 0);
|
|
17511
|
+
const combined = signals.length > 0 ? AbortSignal.any(signals) : void 0;
|
|
17512
|
+
let interrupted;
|
|
17513
|
+
const work = Promise.race([
|
|
17514
|
+
tool.execute(id, dispatchedParams, combined, onUpdate),
|
|
17515
|
+
new Promise((_resolve, reject) => {
|
|
17516
|
+
interrupted = () => reject(new Error("Research time budget elapsed; dispatched work may still settle."));
|
|
17517
|
+
research?.addEventListener("abort", interrupted, { once: true });
|
|
17518
|
+
if (research?.aborted) interrupted();
|
|
17519
|
+
})
|
|
17520
|
+
]).then((value) => referencePages(runtime, tool, dispatchedParams, value)).finally(() => {
|
|
17521
|
+
if (interrupted) research?.removeEventListener("abort", interrupted);
|
|
17522
|
+
});
|
|
17523
|
+
if (key !== void 0) runtime.memo.set(key, { id, value: work });
|
|
17524
|
+
try {
|
|
17525
|
+
const value = await work;
|
|
17526
|
+
if (key !== void 0 && value.isError) runtime.memo.delete(key);
|
|
17527
|
+
return value;
|
|
17528
|
+
} catch (error) {
|
|
17529
|
+
if (key !== void 0) runtime.memo.delete(key);
|
|
17530
|
+
throw error;
|
|
17531
|
+
}
|
|
17532
|
+
}
|
|
17533
|
+
}));
|
|
17534
|
+
if (!runtime.progressive) return allowed;
|
|
17535
|
+
const visible = allowed.filter((tool) => {
|
|
17302
17536
|
const entry = metadata.get(tool.name);
|
|
17303
17537
|
return !entry || entry.tier === "core" || runtime.pinned.has(tool.name) || runtime.loaded.has(entry.tier);
|
|
17304
17538
|
});
|
|
@@ -17379,11 +17613,26 @@ function correctToolNames(runtime, message, visible) {
|
|
|
17379
17613
|
block.name = matches[0];
|
|
17380
17614
|
}
|
|
17381
17615
|
}
|
|
17382
|
-
function withCorrectedToolNames(runtime, response, visible) {
|
|
17616
|
+
function withCorrectedToolNames(runtime, response, visible, wrapUp, signal) {
|
|
17383
17617
|
const result2 = response.result.bind(response);
|
|
17384
17618
|
response.result = async () => {
|
|
17385
17619
|
const message = await result2();
|
|
17620
|
+
const researchEnded = !wrapUp && runtime.research?.signal.aborted && !signal?.aborted;
|
|
17621
|
+
const canFinish = wrapUp && (!signal?.aborted || runtime.hardDeadline);
|
|
17622
|
+
const failedAnswer = canFinish && !runtime.hardDeadline && (message.stopReason === "error" || message.stopReason === "aborted");
|
|
17623
|
+
if (researchEnded) {
|
|
17624
|
+
message.content = message.content.filter((block) => block.type !== "toolCall");
|
|
17625
|
+
if (!message.content.some((block) => block.type === "text" && block.text.trim())) message.content.push({ type: "text", text: "Research time budget elapsed; preparing an answer from the evidence already gathered." });
|
|
17626
|
+
message.stopReason = "stop";
|
|
17627
|
+
delete message.errorMessage;
|
|
17628
|
+
} else if (canFinish && (message.stopReason === "error" || message.stopReason === "aborted" || !message.content.some((block) => block.type === "text" && block.text.trim()))) {
|
|
17629
|
+
message.content.push({ type: "text", text: `I reached this turn's ${runtime.reason === "time-limit" ? "time" : "tool"} limit and could not complete the answer. The results above are partial; ask a narrower question to continue.` });
|
|
17630
|
+
message.stopReason = "stop";
|
|
17631
|
+
delete message.errorMessage;
|
|
17632
|
+
}
|
|
17633
|
+
if (failedAnswer) runtime.reason = "error";
|
|
17386
17634
|
correctToolNames(runtime, message, visible);
|
|
17635
|
+
inheritPageArguments(runtime, message, visible);
|
|
17387
17636
|
return message;
|
|
17388
17637
|
};
|
|
17389
17638
|
return response;
|
|
@@ -17417,7 +17666,7 @@ function explainMissingTool(runtime, message) {
|
|
|
17417
17666
|
function configureAeroRuntime(agent, allowed, limits, progressive = true, pinned = []) {
|
|
17418
17667
|
let runtime = runtimes.get(agent);
|
|
17419
17668
|
if (!runtime) {
|
|
17420
|
-
runtime = { allowed, loaded: /* @__PURE__ */ new Set(), limits: agentTurnLimitsSchema.parse(limits ?? {}), calls: 0, rounds: 0, startedAt: 0, reason: "completed", progressive, pinned: new Set(pinned), corrected: /* @__PURE__ */ new Map(), wrapUp: false };
|
|
17669
|
+
runtime = { allowed, loaded: /* @__PURE__ */ new Set(), limits: agentTurnLimitsSchema.parse(limits ?? {}), calls: 0, rounds: 0, startedAt: 0, reason: "completed", progressive, pinned: new Set(pinned), corrected: /* @__PURE__ */ new Map(), wrapUp: false, hardDeadline: false, memo: /* @__PURE__ */ new Map(), pageReferences: /* @__PURE__ */ new Map(), pageNamespace: randomUUID3().slice(0, 8), pageTurn: 0 };
|
|
17421
17670
|
runtimes.set(agent, runtime);
|
|
17422
17671
|
const state = runtime;
|
|
17423
17672
|
const stream = agent.streamFunction;
|
|
@@ -17425,29 +17674,39 @@ function configureAeroRuntime(agent, allowed, limits, progressive = true, pinned
|
|
|
17425
17674
|
options?.signal?.throwIfAborted();
|
|
17426
17675
|
state.rounds++;
|
|
17427
17676
|
const visible = new Set(getCurrentTools(context.messages).map((tool) => tool.name));
|
|
17428
|
-
const
|
|
17429
|
-
|
|
17677
|
+
const wrapUp = state.wrapUp;
|
|
17678
|
+
const signals = [options?.signal, wrapUp ? void 0 : state.research?.signal].filter((value) => value !== void 0);
|
|
17679
|
+
const messages = [...context.messages];
|
|
17680
|
+
let clockIndex = 0;
|
|
17681
|
+
while (messages[clockIndex]?.role === "system") clockIndex++;
|
|
17682
|
+
messages.splice(clockIndex, 0, {
|
|
17683
|
+
role: "system",
|
|
17684
|
+
timestamp: state.startedAt,
|
|
17685
|
+
content: `Current turn clock (UTC): ${new Date(state.startedAt).toISOString()}. Use this current UTC date and year for relative dates or omitted years unless the user explicitly specifies a year. Stored run and scan dates remain authoritative; this clock is not an observation date.`
|
|
17686
|
+
});
|
|
17687
|
+
const response = stream(model, { ...context, messages }, { ...options, ...signals.length > 0 ? { signal: AbortSignal.any(signals) } : {} });
|
|
17688
|
+
return response instanceof Promise ? response.then((ready) => withCorrectedToolNames(state, ready, visible, wrapUp, options?.signal)) : withCorrectedToolNames(state, response, visible, wrapUp, options?.signal);
|
|
17430
17689
|
};
|
|
17431
17690
|
const before = agent.beforeToolCall;
|
|
17432
17691
|
const after = agent.afterToolCall;
|
|
17433
17692
|
const overLimit = /* @__PURE__ */ new Set();
|
|
17434
17693
|
agent.beforeToolCall = async (event, signal) => {
|
|
17435
|
-
if (signal?.aborted || overLimit.has(event.toolCall.id)) return { block: true, reason: "Turn stopped." };
|
|
17694
|
+
if (signal?.aborted || state.reason === "time-limit" || state.wrapUp || overLimit.has(event.toolCall.id)) return { block: true, reason: "Turn stopped." };
|
|
17436
17695
|
return before?.(event, signal);
|
|
17437
17696
|
};
|
|
17438
17697
|
const finish = agent.finishTurn;
|
|
17439
17698
|
agent.finishTurn = (turn, signal) => {
|
|
17440
|
-
if (state.reason !== "tool-limit") return finish?.(turn, signal);
|
|
17699
|
+
if (state.reason !== "tool-limit" && state.reason !== "time-limit") return finish?.(turn, signal);
|
|
17441
17700
|
if (state.wrapUp) return { action: "end" };
|
|
17442
17701
|
state.wrapUp = true;
|
|
17443
17702
|
return { action: "continue" };
|
|
17444
17703
|
};
|
|
17445
17704
|
const prepare = agent.prepareNextTurnWithContext;
|
|
17446
17705
|
agent.prepareNextTurnWithContext = (turn, signal) => {
|
|
17447
|
-
if (state.reason === "tool-limit" && state.wrapUp) {
|
|
17706
|
+
if ((state.reason === "tool-limit" || state.reason === "time-limit") && state.wrapUp) {
|
|
17448
17707
|
return {
|
|
17449
17708
|
context: { ...turn.context, tools: [] },
|
|
17450
|
-
messages: [{ role: "system", content: TOOL_LIMIT_WRAP_UP, timestamp: Date.now() }]
|
|
17709
|
+
messages: [{ role: "system", content: state.reason === "time-limit" ? TIME_LIMIT_WRAP_UP : TOOL_LIMIT_WRAP_UP, timestamp: Date.now() }]
|
|
17451
17710
|
};
|
|
17452
17711
|
}
|
|
17453
17712
|
return prepare ? prepare(turn, signal) : agent.prepareNextTurn?.(signal);
|
|
@@ -17466,7 +17725,7 @@ function configureAeroRuntime(agent, allowed, limits, progressive = true, pinned
|
|
|
17466
17725
|
agent.subscribe((event) => {
|
|
17467
17726
|
if (event.type === "tool_execution_start") {
|
|
17468
17727
|
if (state.calls >= state.limits.maxToolCalls) {
|
|
17469
|
-
state.reason = "tool-limit";
|
|
17728
|
+
if (state.reason !== "time-limit") state.reason = "tool-limit";
|
|
17470
17729
|
overLimit.add(event.toolCallId);
|
|
17471
17730
|
} else if (!agent.signal?.aborted) {
|
|
17472
17731
|
state.calls++;
|
|
@@ -17497,24 +17756,41 @@ function configureAeroRuntime(agent, allowed, limits, progressive = true, pinned
|
|
|
17497
17756
|
overLimit.clear();
|
|
17498
17757
|
state.corrected.clear();
|
|
17499
17758
|
state.wrapUp = false;
|
|
17759
|
+
state.hardDeadline = false;
|
|
17760
|
+
state.memo.clear();
|
|
17761
|
+
state.pageReferences.clear();
|
|
17762
|
+
state.pageTurn++;
|
|
17763
|
+
state.research = new AbortController();
|
|
17500
17764
|
state.calls = 0;
|
|
17501
17765
|
state.rounds = 0;
|
|
17502
17766
|
state.reason = "completed";
|
|
17503
17767
|
state.startedAt = Date.now();
|
|
17504
17768
|
state.finishedAt = void 0;
|
|
17769
|
+
const reservedMs = Math.min(15e3, Math.max(250, Math.floor(state.limits.timeoutMs / 10)));
|
|
17505
17770
|
state.timer = setTimeout(() => {
|
|
17771
|
+
if (state.wrapUp) return;
|
|
17772
|
+
state.reason = "time-limit";
|
|
17773
|
+
state.research?.abort();
|
|
17774
|
+
}, state.limits.timeoutMs - reservedMs);
|
|
17775
|
+
state.deadlineTimer = setTimeout(() => {
|
|
17506
17776
|
state.reason = "time-limit";
|
|
17777
|
+
state.hardDeadline = true;
|
|
17507
17778
|
agent.abort();
|
|
17508
17779
|
}, state.limits.timeoutMs);
|
|
17509
17780
|
state.timer.unref();
|
|
17781
|
+
state.deadlineTimer.unref();
|
|
17510
17782
|
} else if (event.type === "agent_end") {
|
|
17511
17783
|
state.finishedAt = Date.now();
|
|
17512
17784
|
clearTimeout(state.timer);
|
|
17513
|
-
|
|
17514
|
-
|
|
17785
|
+
clearTimeout(state.deadlineTimer);
|
|
17786
|
+
state.memo.clear();
|
|
17787
|
+
state.pageReferences.clear();
|
|
17788
|
+
if (agent.signal?.aborted && !state.hardDeadline) state.reason = "stopped";
|
|
17789
|
+
if (state.reason === "completed" && agent.state.errorMessage) state.reason = "error";
|
|
17515
17790
|
}
|
|
17516
17791
|
});
|
|
17517
17792
|
}
|
|
17793
|
+
runtime.pageReferences.clear();
|
|
17518
17794
|
runtime.allowed = allowed;
|
|
17519
17795
|
runtime.loaded = /* @__PURE__ */ new Set();
|
|
17520
17796
|
runtime.limits = agentTurnLimitsSchema.parse(limits ?? {});
|
|
@@ -17823,11 +18099,6 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
17823
18099
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
17824
18100
|
var log21 = createLogger("RemoteMcp");
|
|
17825
18101
|
var AERO_EXCLUDED_MCP_TOOLS2 = /* @__PURE__ */ new Set([]);
|
|
17826
|
-
var MAX_TOOL_RESULT_CHARS2 = 2e4;
|
|
17827
|
-
function truncate(text) {
|
|
17828
|
-
if (text.length <= MAX_TOOL_RESULT_CHARS2) return text;
|
|
17829
|
-
return text.slice(0, MAX_TOOL_RESULT_CHARS2) + "\n... (truncated, result too large)";
|
|
17830
|
-
}
|
|
17831
18102
|
async function connectStreamableHttp(server) {
|
|
17832
18103
|
const transport = new StreamableHTTPClientTransport(new URL(server.url), {
|
|
17833
18104
|
requestInit: {
|
|
@@ -17853,8 +18124,23 @@ function adaptRemoteTool(client, tool) {
|
|
|
17853
18124
|
);
|
|
17854
18125
|
const execute = async (_toolCallId, params) => {
|
|
17855
18126
|
const result2 = await client.callTool({ name: tool.name, arguments: params });
|
|
18127
|
+
let shown = result2;
|
|
18128
|
+
if (result2 && typeof result2 === "object") {
|
|
18129
|
+
const envelope = result2;
|
|
18130
|
+
if (envelope.structuredContent !== void 0) shown = envelope.structuredContent;
|
|
18131
|
+
else if (envelope.content?.length === 1 && envelope.content[0]?.type === "text") {
|
|
18132
|
+
const text = envelope.content[0].text ?? "";
|
|
18133
|
+
try {
|
|
18134
|
+
shown = JSON.parse(text);
|
|
18135
|
+
} catch {
|
|
18136
|
+
shown = text;
|
|
18137
|
+
}
|
|
18138
|
+
} else if (envelope.content?.some((block) => block.type === "text")) {
|
|
18139
|
+
shown = { ...result2, content: Object.fromEntries(envelope.content.map((block, index) => [`block_${index + 1}`, block])) };
|
|
18140
|
+
}
|
|
18141
|
+
}
|
|
17856
18142
|
return {
|
|
17857
|
-
content: [{ type: "text", text:
|
|
18143
|
+
content: [{ type: "text", text: truncateToolResult(shown) }],
|
|
17858
18144
|
details: result2,
|
|
17859
18145
|
isError: isRemoteToolError(result2)
|
|
17860
18146
|
};
|
|
@@ -18027,10 +18313,10 @@ var ADVANCED_TOOLS = [
|
|
|
18027
18313
|
var LEGACY_TOOLS = ["canonry_measurement_overview", "canonry_measurement_plan_get", "canonry_run_completeness"];
|
|
18028
18314
|
var SIMPLE_TOOLS = ["canonry_visibility_report"];
|
|
18029
18315
|
var ADVANCED_ROUTES = [
|
|
18030
|
-
"Weakest or strongest Properties: canonry_measurement_portfolio_summary (
|
|
18031
|
-
"Which metros have the biggest gaps: the summary's markets
|
|
18316
|
+
"Weakest or strongest Properties: canonry_measurement_portfolio_summary (mentionRanking for both ends). Compact results carry detailsOmitted; use Property evidence reads for omitted names and cited domains. The default cursor enumerates weakest Properties only; set list=strongest-mentions or list=weakest-mentions to enumerate the chosen ranking. Follow nextCursor with unchanged list and filters; pageList names that selection. Use groupKey or page canonry_measurement_overview for all Property rows.",
|
|
18317
|
+
"Which metros have the biggest gaps: the compact summary's weakestMarkets, whose rates cover full top-level metros. Set list=markets and follow nextCursor with unchanged list and filters for every market; tiedAtWeakest describes only the zero-signal Property cohort, never full metro rates.",
|
|
18032
18318
|
"One Property: canonry_measurement_property_evidence and canonry_measurement_property_competitors (names written instead of it, and citedDomains, its own cited sources). Portfolio lists such as weakestAnswerSources pool many Properties.",
|
|
18033
|
-
'Which names answers give instead across the portfolio: canonry_competitor_landscape with queryClass and runId "latest". Per-Property named-instead lists are samples of weak Properties.',
|
|
18319
|
+
'Which names answers give instead across the portfolio: canonry_competitor_landscape with queryClass and runId "latest", answers=not-mentioned. This selects answers naming none of their targeted Properties; preserve populationSize, answerCount and unknownMentionAnswers. Per-Property named-instead lists are samples of weak Properties.',
|
|
18034
18320
|
'Where answers get their sources: canonry_analytics_sources with queryClass and runId "latest"; without them it pools both classes and every sweep.',
|
|
18035
18321
|
"What changed since the last sweep: canonry_measurement_changes once per class. Quote its distribution for how many Properties moved.",
|
|
18036
18322
|
"Is the sweep complete, or is anything unreliable: canonry_measurement_data_quality (quote completeness expected, executed and missing, unattributedByClass and latestFill), then canonry_run_completeness with its run.displayedRunId for missing answers per engine. A Healthy run status and canonry_doctor are not completeness checks."
|
|
@@ -18060,7 +18346,7 @@ Project shape: an Advanced Measurement plan on the legacy schema v1 (plan revisi
|
|
|
18060
18346
|
prompt: `
|
|
18061
18347
|
|
|
18062
18348
|
Project shape: an Advanced Measurement portfolio (plan revision ${version.revision}) with ${size}, ${queriesIn("branded")} branded and ${queriesIn("non-brand")} non-brand queries. This line settles the project type.
|
|
18063
|
-
Rules: measure per Property, group or market, never pool branded and non-brand, and a query shared by several Properties is one answer scored for each. Denominators count answers (queries x engines), not queries; label them answers. Group Properties only by the metro and submarkets the tools return, never by name. Names given instead were written in the answer text, not cited; cited domains are sources and never go under a named-instead heading. Name only engines a tool returned. Between two sweeps, a Property that moved ${MEASUREMENT_CHANGES_NOISE_ANSWERS} answers or fewer is within noise (withinNoise): never call it a gain, loss, trend or regression. Judge mention and citation separately (mentionWithinNoise, citationWithinNoise): a signal that moved ${MEASUREMENT_CHANGES_NOISE_ANSWERS} answers or fewer is within noise even when the Property's other signal moved more. Quote every coverage figure with its counts, headline and branded figures included; the value text carries them, as in 25.0% (6/24). A Property's citation coverage counts only citations of its own page: 0% does not mean your domain went uncited (weakestAnswerSources.ownDomainAnswers counts that). Property names often carry the brand, so that most listed Properties carry it is not a finding. Properties
|
|
18349
|
+
Rules: measure per Property, group or market, never pool branded and non-brand, and a query shared by several Properties is one answer scored for each. Denominators count answers (queries x engines), not queries; label them answers. Group Properties only by the metro and submarkets the tools return, never by name. Names given instead were written in the answer text, not cited; cited domains are sources and never go under a named-instead heading. Name only engines a tool returned. Between two sweeps, a Property that moved ${MEASUREMENT_CHANGES_NOISE_ANSWERS} answers or fewer is within noise (withinNoise): never call it a gain, loss, trend or regression. Judge mention and citation separately (mentionWithinNoise, citationWithinNoise): a signal that moved ${MEASUREMENT_CHANGES_NOISE_ANSWERS} answers or fewer is within noise even when the Property's other signal moved more. Quote every coverage figure with its counts, headline and branded figures included; the value text carries them, as in 25.0% (6/24). A Property's citation coverage counts only citations of its own page: 0% does not mean your domain went uncited (weakestAnswerSources.ownDomainAnswers counts that). Property names often carry the brand, so that most listed Properties carry it is not a finding. tiedAtWeakest counts Properties with zero mentions and zero citations, separate from propertiesNeverMentioned (which may include cited Properties). Say how many tie (tiedAtWeakest.count); compact results omit the cohort's byMetro details. Call listed tie rows examples, not a ranked bottom list. A result with truncated true, a total above its rows, __partialLists or __truncation is partial: say how many of how many you saw, and never call those rows the biggest, all, or the full picture.
|
|
18064
18350
|
Route each question to its read:
|
|
18065
18351
|
${ADVANCED_ROUTES.map((route) => `- ${route}`).join("\n")}
|
|
18066
18352
|
canonry_visibility_report describes the whole project, not one Property. canonry_measurement_plan_get is plan structure with no metrics; do not read it for analysis.`,
|
|
@@ -21039,7 +21325,7 @@ function cloneGtmConfig(config) {
|
|
|
21039
21325
|
function createGoogleMarketingConfigCredentialStore(input) {
|
|
21040
21326
|
const persistConfigPatch = input.saveConfigPatch ?? saveConfigPatch;
|
|
21041
21327
|
const env = input.env ?? process.env;
|
|
21042
|
-
const
|
|
21328
|
+
const randomUUID4 = input.randomUUID ?? crypto29.randomUUID;
|
|
21043
21329
|
const snapshot = () => ({
|
|
21044
21330
|
googleAds: cloneGoogleAdsConfig(input.config.googleAds),
|
|
21045
21331
|
gtm: cloneGtmConfig(input.config.gtm)
|
|
@@ -21087,7 +21373,7 @@ function createGoogleMarketingConfigCredentialStore(input) {
|
|
|
21087
21373
|
upsertGoogleAdsConnection(input.config, {
|
|
21088
21374
|
projectId: project.id,
|
|
21089
21375
|
projectName: project.name,
|
|
21090
|
-
credentialGeneration:
|
|
21376
|
+
credentialGeneration: randomUUID4(),
|
|
21091
21377
|
...credential.accessToken ? { accessToken: credential.accessToken } : {},
|
|
21092
21378
|
refreshToken: credential.refreshToken ?? null,
|
|
21093
21379
|
tokenExpiresAt: credential.expiresAt ?? null,
|
|
@@ -21099,7 +21385,7 @@ function createGoogleMarketingConfigCredentialStore(input) {
|
|
|
21099
21385
|
upsertGtmConnection(input.config, {
|
|
21100
21386
|
projectId: project.id,
|
|
21101
21387
|
projectName: project.name,
|
|
21102
|
-
credentialGeneration:
|
|
21388
|
+
credentialGeneration: randomUUID4(),
|
|
21103
21389
|
...credential.accessToken ? { accessToken: credential.accessToken } : {},
|
|
21104
21390
|
refreshToken: credential.refreshToken ?? null,
|
|
21105
21391
|
tokenExpiresAt: credential.expiresAt ?? null,
|