hive-intelligence 1.3.0 → 1.5.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 +2 -2
- package/agent-skills/SKILL.md +1 -1
- package/agent-skills/packs/hive-build/SKILL.md +1 -1
- package/agent-skills/packs/hive-build-onboarding/SKILL.md +7 -7
- package/agent-skills/packs/hive-cli/SKILL.md +2 -2
- package/agent-skills/packs/hive-defi-research/SKILL.md +1 -1
- package/agent-skills/packs/hive-dex-pool-analysis/SKILL.md +1 -1
- package/agent-skills/packs/hive-market-research/SKILL.md +1 -1
- package/agent-skills/packs/hive-mcp/SKILL.md +1 -1
- package/agent-skills/packs/hive-network-infrastructure/SKILL.md +1 -1
- package/agent-skills/packs/hive-nft-research/SKILL.md +1 -1
- package/agent-skills/packs/hive-prediction-markets/SKILL.md +1 -1
- package/agent-skills/packs/hive-query/SKILL.md +1 -1
- package/agent-skills/packs/hive-security-risk/SKILL.md +1 -1
- package/agent-skills/packs/hive-solana-analysis/SKILL.md +1 -1
- package/agent-skills/packs/hive-stateful-monitoring/SKILL.md +1 -1
- package/agent-skills/packs/hive-token-diligence/SKILL.md +1 -1
- package/agent-skills/packs/hive-tool-discovery/SKILL.md +1 -1
- package/agent-skills/packs/hive-wallet-investigation/SKILL.md +1 -1
- package/build/{api-client-FTVDFW5V.js → api-client-WMWFWXC7.js} +3 -15
- package/build/{auth-EZFJ5RYB.js → auth-Y4GXW376.js} +1 -9
- package/build/browser-auth-F62BMD7W.js +20 -0
- package/build/{chunk-W2QL7LYI.js → chunk-AGM2CIRL.js} +971 -117
- package/build/{chunk-7GD2MFYI.js → chunk-BDO27NIP.js} +37 -17
- package/build/{upgrade-J4HIGCKE.js → chunk-GB4XMTDE.js} +3 -4
- package/build/{chunk-TNQ7GJ6Q.js → chunk-GWZIAAQ2.js} +58 -40
- package/build/{chunk-Q7VBYLX7.js → chunk-P7NLFSZQ.js} +0 -58
- package/build/{chunk-N32UIHBU.js → chunk-SFN772RL.js} +5 -5
- package/build/{chunk-AEU43ACS.js → chunk-TVBPCELA.js} +2 -2
- package/build/{chunk-RUHO24SX.js → chunk-WF34ZD5I.js} +7 -162
- package/build/{chunk-I5Z6FZRL.js → chunk-XQPEFCSY.js} +14 -12
- package/build/cli.js +36 -47
- package/build/{completion-7KBW243H.js → completion-C57PDJMY.js} +1 -1
- package/build/{doctor-ORDOCGLM.js → doctor-XG5IQCXK.js} +5 -5
- package/build/{init-UCTCUYFA.js → init-COHA3VZI.js} +3 -3
- package/build/{init-all-BVWW3JVK.js → init-all-YTQJLPLR.js} +43 -24
- package/build/{mcpServer-RUEOTTF7.js → mcpServer-XHIN5JER.js} +1 -1
- package/build/monitor-worker.js +2 -2
- package/build/{namespace-L5DGCY7K.js → namespace-YLCGHVRT.js} +1 -1
- package/build/release.json +4 -4
- package/build/{serve-DKLR2WQH.js → serve-PWMT5IRU.js} +1 -1
- package/build/server.js +1475 -452
- package/build/stdio.js +1 -1
- package/build/{tools-I7GLWTV6.js → tools-XNFJBN7Q.js} +2 -2
- package/build/{uninstall-SAOIPL6K.js → uninstall-TKDP6C4F.js} +7 -7
- package/build/upgrade-ODPKMIDC.js +14 -0
- package/build/{watch-WVR4CGCD.js → watch-MXJSNGL2.js} +2 -2
- package/package.json +5 -3
- package/build/browser-auth-OJSUNFBI.js +0 -8
|
@@ -17608,11 +17608,11 @@ var require_client = __commonJS({
|
|
|
17608
17608
|
};
|
|
17609
17609
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17610
17610
|
exports.TerminatedCloseEvent = void 0;
|
|
17611
|
-
exports.createClient =
|
|
17611
|
+
exports.createClient = createClient3;
|
|
17612
17612
|
var common_1 = require_common();
|
|
17613
17613
|
var utils_1 = require_utils();
|
|
17614
17614
|
__exportStar(require_common(), exports);
|
|
17615
|
-
function
|
|
17615
|
+
function createClient3(options) {
|
|
17616
17616
|
const {
|
|
17617
17617
|
url,
|
|
17618
17618
|
connectionParams,
|
|
@@ -20827,7 +20827,7 @@ var require_websocket = __commonJS({
|
|
|
20827
20827
|
var http = __require("http");
|
|
20828
20828
|
var net2 = __require("net");
|
|
20829
20829
|
var tls = __require("tls");
|
|
20830
|
-
var { randomBytes: randomBytes4, createHash:
|
|
20830
|
+
var { randomBytes: randomBytes4, createHash: createHash8 } = __require("crypto");
|
|
20831
20831
|
var { Duplex, Readable } = __require("stream");
|
|
20832
20832
|
var { URL: URL2 } = __require("url");
|
|
20833
20833
|
var PerMessageDeflate = require_permessage_deflate();
|
|
@@ -21495,7 +21495,7 @@ var require_websocket = __commonJS({
|
|
|
21495
21495
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
21496
21496
|
return;
|
|
21497
21497
|
}
|
|
21498
|
-
const digest2 =
|
|
21498
|
+
const digest2 = createHash8("sha1").update(key + GUID).digest("base64");
|
|
21499
21499
|
if (res.headers["sec-websocket-accept"] !== digest2) {
|
|
21500
21500
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
21501
21501
|
return;
|
|
@@ -21864,7 +21864,7 @@ var require_websocket_server = __commonJS({
|
|
|
21864
21864
|
var EventEmitter = __require("events");
|
|
21865
21865
|
var http = __require("http");
|
|
21866
21866
|
var { Duplex } = __require("stream");
|
|
21867
|
-
var { createHash:
|
|
21867
|
+
var { createHash: createHash8 } = __require("crypto");
|
|
21868
21868
|
var extension = require_extension();
|
|
21869
21869
|
var PerMessageDeflate = require_permessage_deflate();
|
|
21870
21870
|
var subprotocol = require_subprotocol();
|
|
@@ -22171,7 +22171,7 @@ var require_websocket_server = __commonJS({
|
|
|
22171
22171
|
);
|
|
22172
22172
|
}
|
|
22173
22173
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
22174
|
-
const digest2 =
|
|
22174
|
+
const digest2 = createHash8("sha1").update(key + GUID).digest("base64");
|
|
22175
22175
|
const headers = [
|
|
22176
22176
|
"HTTP/1.1 101 Switching Protocols",
|
|
22177
22177
|
"Upgrade: websocket",
|
|
@@ -25358,6 +25358,11 @@ function getPort() {
|
|
|
25358
25358
|
function getRateLimitMax() {
|
|
25359
25359
|
return rateLimitMaxSchema.parse(process.env.RATE_LIMIT_MAX ?? 30);
|
|
25360
25360
|
}
|
|
25361
|
+
function getCredentialedRateLimitMax() {
|
|
25362
|
+
return rateLimitMaxSchema.parse(
|
|
25363
|
+
process.env.CREDENTIALED_RATE_LIMIT_MAX ?? 1500
|
|
25364
|
+
);
|
|
25365
|
+
}
|
|
25361
25366
|
function getCorsAllowedOrigins() {
|
|
25362
25367
|
return getCsvEnv("CORS_ALLOWED_ORIGINS");
|
|
25363
25368
|
}
|
|
@@ -32582,7 +32587,7 @@ Don't use when:
|
|
|
32582
32587
|
|
|
32583
32588
|
Error Handling:
|
|
32584
32589
|
- Returns 404 if NFT collection ID not found
|
|
32585
|
-
- Requires Analyst plan or higher
|
|
32590
|
+
- Requires CoinGecko's Analyst plan or higher (a provider tier, not a Hive plan)
|
|
32586
32591
|
- Rate limited to 10-50 calls/minute depending on API plan`,
|
|
32587
32592
|
inputSchema: {
|
|
32588
32593
|
type: "object",
|
|
@@ -32700,7 +32705,7 @@ Don't use when:
|
|
|
32700
32705
|
- You need historical NFT data (use coingecko_get_nft_market_chart)
|
|
32701
32706
|
|
|
32702
32707
|
Error Handling:
|
|
32703
|
-
- Requires Analyst plan or higher
|
|
32708
|
+
- Requires CoinGecko's Analyst plan or higher (a provider tier, not a Hive plan)
|
|
32704
32709
|
- Rate limited to 10-50 calls/minute depending on API plan`,
|
|
32705
32710
|
inputSchema: {
|
|
32706
32711
|
type: "object",
|
|
@@ -33112,7 +33117,7 @@ Don't use when:
|
|
|
33112
33117
|
|
|
33113
33118
|
Error Handling:
|
|
33114
33119
|
- Returns 404 if network or token address not found
|
|
33115
|
-
- Requires Analyst plan or higher
|
|
33120
|
+
- Requires CoinGecko's Analyst plan or higher (a provider tier, not a Hive plan)
|
|
33116
33121
|
- Not all networks support trader tracking
|
|
33117
33122
|
- Rate limited based on API plan`,
|
|
33118
33123
|
inputSchema: {
|
|
@@ -33187,7 +33192,7 @@ Don't use when:
|
|
|
33187
33192
|
|
|
33188
33193
|
Error Handling:
|
|
33189
33194
|
- Returns 404 if contract not found on the specified platform
|
|
33190
|
-
- Requires Analyst plan or higher
|
|
33195
|
+
- Requires CoinGecko's Analyst plan or higher (a provider tier, not a Hive plan)
|
|
33191
33196
|
- Rate limited based on API plan`,
|
|
33192
33197
|
inputSchema: {
|
|
33193
33198
|
type: "object",
|
|
@@ -33824,7 +33829,6 @@ var get_insights_default = { metadata: metadata88, tool: tool88, handler: handle
|
|
|
33824
33829
|
// src/db/supabase.ts
|
|
33825
33830
|
import { createClient } from "@supabase/supabase-js";
|
|
33826
33831
|
var _supabaseAdmin = null;
|
|
33827
|
-
var _supabaseAuth = null;
|
|
33828
33832
|
function isSupabaseConfigured() {
|
|
33829
33833
|
return hasEnvVars("SUPABASE_URL", "SUPABASE_SERVICE_ROLE_KEY");
|
|
33830
33834
|
}
|
|
@@ -33843,21 +33847,6 @@ function getSupabaseAdmin() {
|
|
|
33843
33847
|
}
|
|
33844
33848
|
return _supabaseAdmin;
|
|
33845
33849
|
}
|
|
33846
|
-
function getSupabaseAuthClient() {
|
|
33847
|
-
if (!_supabaseAuth) {
|
|
33848
|
-
if (!hasEnvVars("SUPABASE_URL", "SUPABASE_ANON_KEY")) {
|
|
33849
|
-
throw new Error(
|
|
33850
|
-
"Supabase auth client not configured. Set SUPABASE_URL and SUPABASE_ANON_KEY."
|
|
33851
|
-
);
|
|
33852
|
-
}
|
|
33853
|
-
_supabaseAuth = createClient(
|
|
33854
|
-
getRequiredEnvVar("SUPABASE_URL"),
|
|
33855
|
-
getRequiredEnvVar("SUPABASE_ANON_KEY"),
|
|
33856
|
-
{ auth: { persistSession: false, autoRefreshToken: false } }
|
|
33857
|
-
);
|
|
33858
|
-
}
|
|
33859
|
-
return _supabaseAuth;
|
|
33860
|
-
}
|
|
33861
33850
|
|
|
33862
33851
|
// src/utils/secureLogger.ts
|
|
33863
33852
|
var SENSITIVE_PATTERNS = [
|
|
@@ -33894,7 +33883,7 @@ var SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
|
33894
33883
|
"GOPLUS_API_KEY"
|
|
33895
33884
|
]);
|
|
33896
33885
|
function isSensitiveEnvironmentKey(key) {
|
|
33897
|
-
return /(?:api[_-]?(?:key|id)|key|secret|password|token|auth|credential|private|bearer|redis.*url)/i.test(
|
|
33886
|
+
return /(?:api[_-]?(?:key|id)|key|secret|password|token|auth|credential|private|bearer|redis.*url|sharpe_archive_supabase)/i.test(
|
|
33898
33887
|
key
|
|
33899
33888
|
);
|
|
33900
33889
|
}
|
|
@@ -34497,6 +34486,23 @@ async function listSubjectAuditEvents(args) {
|
|
|
34497
34486
|
}
|
|
34498
34487
|
return { ok: true, data: data ?? [] };
|
|
34499
34488
|
}
|
|
34489
|
+
function buildStatefulWriteAuditEvent(input) {
|
|
34490
|
+
if (!input.isStatefulWrite) return null;
|
|
34491
|
+
const userId = input.subjectContext?.userId ?? input.userId;
|
|
34492
|
+
if (!userId) return null;
|
|
34493
|
+
return {
|
|
34494
|
+
userId,
|
|
34495
|
+
subjectId: input.subjectContext?.subjectId,
|
|
34496
|
+
keyId: input.keyId,
|
|
34497
|
+
transport: input.transport,
|
|
34498
|
+
toolName: input.toolName,
|
|
34499
|
+
requestId: input.requestId,
|
|
34500
|
+
metadata: {
|
|
34501
|
+
...input.authMethod ? { auth_method: input.authMethod } : {},
|
|
34502
|
+
...input.authMethod === "oauth" ? { oauth_client_id: input.oauthClientId ?? null } : {}
|
|
34503
|
+
}
|
|
34504
|
+
};
|
|
34505
|
+
}
|
|
34500
34506
|
function recordSubjectAuditEvent(args) {
|
|
34501
34507
|
if (!isSupabaseConfigured()) return;
|
|
34502
34508
|
getSupabaseAdmin().from("state_subject_audit_events").insert({
|
|
@@ -46384,8 +46390,8 @@ Don't use when:
|
|
|
46384
46390
|
}
|
|
46385
46391
|
if (tokens) filterParams.tokens = tokens;
|
|
46386
46392
|
const MAX_SAFE_LIMIT = RESPONSE_LIMITS.MAX_LIMITS.tokens;
|
|
46387
|
-
const
|
|
46388
|
-
const requestedLimit = limit ||
|
|
46393
|
+
const DEFAULT_LIMIT3 = RESPONSE_LIMITS.DEFAULT_LIMITS.tokens;
|
|
46394
|
+
const requestedLimit = limit || DEFAULT_LIMIT3;
|
|
46389
46395
|
const finalLimit = Math.min(requestedLimit, MAX_SAFE_LIMIT);
|
|
46390
46396
|
filterParams.limit = finalLimit;
|
|
46391
46397
|
filterParams.offset = offset || 0;
|
|
@@ -51866,8 +51872,8 @@ Error Handling:
|
|
|
51866
51872
|
},
|
|
51867
51873
|
async execute(args) {
|
|
51868
51874
|
const MAX_SAFE_LIMIT = 50;
|
|
51869
|
-
const
|
|
51870
|
-
const limit = Math.min(args?.limit ||
|
|
51875
|
+
const DEFAULT_LIMIT3 = 30;
|
|
51876
|
+
const limit = Math.min(args?.limit || DEFAULT_LIMIT3, MAX_SAFE_LIMIT);
|
|
51871
51877
|
const offset = args?.offset || 0;
|
|
51872
51878
|
try {
|
|
51873
51879
|
const data = await apiClient.getProtocols();
|
|
@@ -52975,8 +52981,8 @@ Error Handling:
|
|
|
52975
52981
|
},
|
|
52976
52982
|
async execute(args) {
|
|
52977
52983
|
const MAX_SAFE_LIMIT = 100;
|
|
52978
|
-
const
|
|
52979
|
-
const limit = Math.min(args?.limit ||
|
|
52984
|
+
const DEFAULT_LIMIT3 = 50;
|
|
52985
|
+
const limit = Math.min(args?.limit || DEFAULT_LIMIT3, MAX_SAFE_LIMIT);
|
|
52980
52986
|
const offset = args?.offset || 0;
|
|
52981
52987
|
const minTvl = args?.minTvl || 0;
|
|
52982
52988
|
try {
|
|
@@ -55092,8 +55098,8 @@ Error Handling:
|
|
|
55092
55098
|
},
|
|
55093
55099
|
async execute(args) {
|
|
55094
55100
|
const MAX_SAFE_LIMIT = 100;
|
|
55095
|
-
const
|
|
55096
|
-
const limit = Math.min(args.limit ||
|
|
55101
|
+
const DEFAULT_LIMIT3 = 50;
|
|
55102
|
+
const limit = Math.min(args.limit || DEFAULT_LIMIT3, MAX_SAFE_LIMIT);
|
|
55097
55103
|
const offset = args.offset || 0;
|
|
55098
55104
|
const minFees = args.minFees || 0;
|
|
55099
55105
|
try {
|
|
@@ -80540,6 +80546,594 @@ Don't use when:
|
|
|
80540
80546
|
];
|
|
80541
80547
|
}
|
|
80542
80548
|
|
|
80549
|
+
// src/tools/archive/client.ts
|
|
80550
|
+
import { createClient as createClient2 } from "@supabase/supabase-js";
|
|
80551
|
+
var ARCHIVE_URL_ENV = "SHARPE_ARCHIVE_SUPABASE_URL";
|
|
80552
|
+
var ARCHIVE_KEY_ENV = "SHARPE_ARCHIVE_SUPABASE_SERVICE_ROLE_KEY";
|
|
80553
|
+
var ARCHIVE_MISSING_CREDENTIALS_MESSAGE = `Hive Archive credentials not configured \u2014 set ${ARCHIVE_URL_ENV} and ${ARCHIVE_KEY_ENV} to enable archive_ history tools. Until then use the live derivatives tools (ccxt_fetch_funding_rates_multi, hyperliquid_get_funding_history) for current data.`;
|
|
80554
|
+
var _archiveClient = null;
|
|
80555
|
+
function isArchiveConfigured() {
|
|
80556
|
+
return hasEnvVars(ARCHIVE_URL_ENV, ARCHIVE_KEY_ENV);
|
|
80557
|
+
}
|
|
80558
|
+
function getArchiveClient() {
|
|
80559
|
+
if (!isArchiveConfigured()) return null;
|
|
80560
|
+
if (!_archiveClient) {
|
|
80561
|
+
_archiveClient = createClient2(
|
|
80562
|
+
getEnvVar(ARCHIVE_URL_ENV),
|
|
80563
|
+
getEnvVar(ARCHIVE_KEY_ENV),
|
|
80564
|
+
{ auth: { persistSession: false, autoRefreshToken: false } }
|
|
80565
|
+
);
|
|
80566
|
+
}
|
|
80567
|
+
return _archiveClient;
|
|
80568
|
+
}
|
|
80569
|
+
|
|
80570
|
+
// src/tools/archive/api.ts
|
|
80571
|
+
var DEFAULT_LIMIT2 = 100;
|
|
80572
|
+
var MAX_LIMIT2 = 500;
|
|
80573
|
+
var ARCHIVE_SINCE_NOTE = "archive since 2023-11";
|
|
80574
|
+
var QUERY_TIMEOUT_MS = 1e4;
|
|
80575
|
+
var VENUE_SCAN_MAX_STEPS = 64;
|
|
80576
|
+
var COVERAGE_CACHE_TTL_MS = 10 * 60 * 1e3;
|
|
80577
|
+
var COVERAGE_CACHE_MAX_ENTRIES = 16;
|
|
80578
|
+
var HOURS_PER_YEAR2 = 8760;
|
|
80579
|
+
function annualizeRate(rate, intervalHours) {
|
|
80580
|
+
if (!Number.isFinite(rate)) return null;
|
|
80581
|
+
if (!Number.isFinite(intervalHours) || intervalHours <= 0) return null;
|
|
80582
|
+
return rate * (HOURS_PER_YEAR2 / intervalHours);
|
|
80583
|
+
}
|
|
80584
|
+
function clampLimit3(value) {
|
|
80585
|
+
const parsed = typeof value === "number" ? Math.floor(value) : NaN;
|
|
80586
|
+
if (!Number.isFinite(parsed)) return DEFAULT_LIMIT2;
|
|
80587
|
+
return Math.min(Math.max(parsed, 1), MAX_LIMIT2);
|
|
80588
|
+
}
|
|
80589
|
+
function escapeLikeLiteral(value) {
|
|
80590
|
+
return value.replace(/[\\%_]/g, (match) => `\\${match}`);
|
|
80591
|
+
}
|
|
80592
|
+
var ArchiveInputError = class extends Error {
|
|
80593
|
+
};
|
|
80594
|
+
function normalizeBaseCoin(value) {
|
|
80595
|
+
const coin = typeof value === "string" ? value.trim().toUpperCase() : "";
|
|
80596
|
+
if (!coin) {
|
|
80597
|
+
throw new ArchiveInputError(
|
|
80598
|
+
'base_coin is required \u2014 pass the base asset ticker, e.g. "BTC" or "ETH". Call archive_get_coverage to see which venues and datasets the archive holds.'
|
|
80599
|
+
);
|
|
80600
|
+
}
|
|
80601
|
+
return coin;
|
|
80602
|
+
}
|
|
80603
|
+
function normalizeExchange(value) {
|
|
80604
|
+
if (typeof value !== "string") return null;
|
|
80605
|
+
const exchange = value.trim();
|
|
80606
|
+
return exchange === "" ? null : exchange;
|
|
80607
|
+
}
|
|
80608
|
+
function normalizeIsoDate(value, field) {
|
|
80609
|
+
if (value === void 0 || value === null) return null;
|
|
80610
|
+
if (typeof value !== "string" || Number.isNaN(Date.parse(value))) {
|
|
80611
|
+
throw new ArchiveInputError(
|
|
80612
|
+
`${field} must be an ISO 8601 datetime (e.g. "2024-01-01T00:00:00Z"); got ${JSON.stringify(value)}.`
|
|
80613
|
+
);
|
|
80614
|
+
}
|
|
80615
|
+
return value;
|
|
80616
|
+
}
|
|
80617
|
+
function parseHistoryArgs(args) {
|
|
80618
|
+
const from = normalizeIsoDate(args?.from, "from");
|
|
80619
|
+
const to = normalizeIsoDate(args?.to, "to");
|
|
80620
|
+
if (from && to && Date.parse(from) > Date.parse(to)) {
|
|
80621
|
+
throw new ArchiveInputError(
|
|
80622
|
+
`from (${from}) is after to (${to}) \u2014 swap the bounds.`
|
|
80623
|
+
);
|
|
80624
|
+
}
|
|
80625
|
+
return {
|
|
80626
|
+
base_coin: normalizeBaseCoin(args?.base_coin),
|
|
80627
|
+
exchange: normalizeExchange(args?.exchange),
|
|
80628
|
+
from,
|
|
80629
|
+
to,
|
|
80630
|
+
limit: clampLimit3(args?.limit)
|
|
80631
|
+
};
|
|
80632
|
+
}
|
|
80633
|
+
function archiveQueryError(context, error) {
|
|
80634
|
+
const detail = error.message ?? "unknown error";
|
|
80635
|
+
const code = error.code ? ` (code ${error.code})` : "";
|
|
80636
|
+
if (error.code === "57014" || /timeout/i.test(detail)) {
|
|
80637
|
+
return new Error(
|
|
80638
|
+
`Hive Archive query timed out while ${context}${code}. Narrow the time range with from/to or lower limit, then retry.`
|
|
80639
|
+
);
|
|
80640
|
+
}
|
|
80641
|
+
if (/jwt|apikey|api key|authorization/i.test(detail)) {
|
|
80642
|
+
return new Error(
|
|
80643
|
+
`Hive Archive rejected the configured credentials while ${context}${code}: ${detail}. The archive key needs rotation \u2014 this is a server configuration issue, not a query problem.`
|
|
80644
|
+
);
|
|
80645
|
+
}
|
|
80646
|
+
return new Error(
|
|
80647
|
+
`Hive Archive query failed while ${context}${code}: ${detail}. Retry once; if it persists the archive is degraded \u2014 use live derivatives tools instead.`
|
|
80648
|
+
);
|
|
80649
|
+
}
|
|
80650
|
+
var HISTORY_TABLES = {
|
|
80651
|
+
funding: {
|
|
80652
|
+
table: "funding_rates_history",
|
|
80653
|
+
columns: "exchange,symbol,base_coin,rate,interval_hours,settled_at",
|
|
80654
|
+
tsColumn: "settled_at"
|
|
80655
|
+
},
|
|
80656
|
+
open_interest: {
|
|
80657
|
+
table: "futures_open_interest",
|
|
80658
|
+
columns: "exchange,base_coin,symbol,open_interest_value,timestamp",
|
|
80659
|
+
tsColumn: "timestamp"
|
|
80660
|
+
},
|
|
80661
|
+
basis: {
|
|
80662
|
+
table: "futures_basis",
|
|
80663
|
+
columns: "exchange,base_coin,spot_price,futures_price,basis_rate,annualized_basis,timestamp",
|
|
80664
|
+
tsColumn: "timestamp"
|
|
80665
|
+
},
|
|
80666
|
+
long_short: {
|
|
80667
|
+
table: "futures_long_short_ratio",
|
|
80668
|
+
columns: "exchange,base_coin,symbol,long_ratio,short_ratio,timestamp",
|
|
80669
|
+
tsColumn: "timestamp"
|
|
80670
|
+
},
|
|
80671
|
+
liquidations: {
|
|
80672
|
+
table: "futures_liquidations",
|
|
80673
|
+
columns: "exchange,base_coin,symbol,side,quantity,price,timestamp",
|
|
80674
|
+
tsColumn: "timestamp"
|
|
80675
|
+
}
|
|
80676
|
+
};
|
|
80677
|
+
async function fetchHistoryRows(client2, dataset, filters) {
|
|
80678
|
+
const spec = HISTORY_TABLES[dataset];
|
|
80679
|
+
let query = client2.from(spec.table).select(spec.columns).eq("base_coin", filters.base_coin);
|
|
80680
|
+
if (filters.exchange) {
|
|
80681
|
+
query = query.ilike("exchange", escapeLikeLiteral(filters.exchange));
|
|
80682
|
+
}
|
|
80683
|
+
if (filters.from) query = query.gte(spec.tsColumn, filters.from);
|
|
80684
|
+
if (filters.to) query = query.lte(spec.tsColumn, filters.to);
|
|
80685
|
+
const { data, error } = await query.order(spec.tsColumn, { ascending: false }).limit(filters.limit).abortSignal(AbortSignal.timeout(QUERY_TIMEOUT_MS));
|
|
80686
|
+
if (error) {
|
|
80687
|
+
throw archiveQueryError(
|
|
80688
|
+
`reading ${spec.table} for ${filters.base_coin}`,
|
|
80689
|
+
error
|
|
80690
|
+
);
|
|
80691
|
+
}
|
|
80692
|
+
return data ?? [];
|
|
80693
|
+
}
|
|
80694
|
+
var coverageCache = /* @__PURE__ */ new Map();
|
|
80695
|
+
async function probeTimestamp(client2, spec, direction, baseCoin) {
|
|
80696
|
+
let query = client2.from(spec.table).select(spec.tsColumn);
|
|
80697
|
+
if (baseCoin) query = query.eq("base_coin", baseCoin);
|
|
80698
|
+
const { data, error } = await query.order(spec.tsColumn, { ascending: direction === "asc" }).limit(1).abortSignal(AbortSignal.timeout(QUERY_TIMEOUT_MS));
|
|
80699
|
+
if (error) {
|
|
80700
|
+
throw archiveQueryError(`probing ${spec.table} coverage`, error);
|
|
80701
|
+
}
|
|
80702
|
+
const row = (data ?? [])[0];
|
|
80703
|
+
const value = row?.[spec.tsColumn];
|
|
80704
|
+
return typeof value === "string" ? value : null;
|
|
80705
|
+
}
|
|
80706
|
+
async function listFundingVenues(client2) {
|
|
80707
|
+
const venues = [];
|
|
80708
|
+
let cursor = null;
|
|
80709
|
+
for (let step2 = 0; step2 < VENUE_SCAN_MAX_STEPS; step2++) {
|
|
80710
|
+
let query = client2.from("funding_rates_history").select("exchange");
|
|
80711
|
+
if (cursor !== null) query = query.gt("exchange", cursor);
|
|
80712
|
+
const { data, error } = await query.order("exchange", { ascending: true }).limit(1).abortSignal(AbortSignal.timeout(QUERY_TIMEOUT_MS));
|
|
80713
|
+
if (error) throw archiveQueryError("enumerating archive venues", error);
|
|
80714
|
+
const row = (data ?? [])[0];
|
|
80715
|
+
if (!row || typeof row.exchange !== "string") {
|
|
80716
|
+
return { venues, truncated: false };
|
|
80717
|
+
}
|
|
80718
|
+
venues.push(row.exchange);
|
|
80719
|
+
cursor = row.exchange;
|
|
80720
|
+
}
|
|
80721
|
+
return { venues, truncated: true };
|
|
80722
|
+
}
|
|
80723
|
+
async function fetchCoverage(client2, probeBaseCoin) {
|
|
80724
|
+
const cached = coverageCache.get(probeBaseCoin);
|
|
80725
|
+
if (cached && Date.now() - cached.at < COVERAGE_CACHE_TTL_MS) {
|
|
80726
|
+
return { coverage: cached.payload, cache: "hit" };
|
|
80727
|
+
}
|
|
80728
|
+
const futuresDatasets = [
|
|
80729
|
+
"open_interest",
|
|
80730
|
+
"basis",
|
|
80731
|
+
"long_short",
|
|
80732
|
+
"liquidations"
|
|
80733
|
+
];
|
|
80734
|
+
const [venueScan, fundingEarliest, fundingLatest, ...futuresExtremes] = await Promise.all([
|
|
80735
|
+
listFundingVenues(client2),
|
|
80736
|
+
probeTimestamp(client2, HISTORY_TABLES.funding, "asc", null),
|
|
80737
|
+
probeTimestamp(client2, HISTORY_TABLES.funding, "desc", null),
|
|
80738
|
+
...futuresDatasets.flatMap((dataset) => [
|
|
80739
|
+
probeTimestamp(client2, HISTORY_TABLES[dataset], "asc", probeBaseCoin),
|
|
80740
|
+
probeTimestamp(client2, HISTORY_TABLES[dataset], "desc", probeBaseCoin)
|
|
80741
|
+
])
|
|
80742
|
+
]);
|
|
80743
|
+
const datasets = [
|
|
80744
|
+
{
|
|
80745
|
+
dataset: "funding",
|
|
80746
|
+
table: HISTORY_TABLES.funding.table,
|
|
80747
|
+
scope: "all_coins",
|
|
80748
|
+
earliest_at: fundingEarliest,
|
|
80749
|
+
latest_at: fundingLatest
|
|
80750
|
+
},
|
|
80751
|
+
...futuresDatasets.map((dataset, index) => {
|
|
80752
|
+
return {
|
|
80753
|
+
dataset,
|
|
80754
|
+
table: HISTORY_TABLES[dataset].table,
|
|
80755
|
+
scope: "probe_coin",
|
|
80756
|
+
earliest_at: futuresExtremes[index * 2],
|
|
80757
|
+
latest_at: futuresExtremes[index * 2 + 1]
|
|
80758
|
+
};
|
|
80759
|
+
})
|
|
80760
|
+
];
|
|
80761
|
+
const coverage = {
|
|
80762
|
+
venues: venueScan.venues,
|
|
80763
|
+
venue_count: venueScan.venues.length,
|
|
80764
|
+
venues_truncated: venueScan.truncated,
|
|
80765
|
+
probe_base_coin: probeBaseCoin,
|
|
80766
|
+
datasets,
|
|
80767
|
+
note: `${ARCHIVE_SINCE_NOTE} (funding settlements). Futures datasets retain less history and their extremes are probed for probe_base_coin via the (base_coin, timestamp) indexes \u2014 pass base_coin to probe another asset. Venues are the all-time distinct funding exchanges.`
|
|
80768
|
+
};
|
|
80769
|
+
if (coverageCache.size >= COVERAGE_CACHE_MAX_ENTRIES) {
|
|
80770
|
+
const oldestKey = coverageCache.keys().next().value;
|
|
80771
|
+
if (oldestKey !== void 0) coverageCache.delete(oldestKey);
|
|
80772
|
+
}
|
|
80773
|
+
coverageCache.set(probeBaseCoin, { at: Date.now(), payload: coverage });
|
|
80774
|
+
return { coverage, cache: "miss" };
|
|
80775
|
+
}
|
|
80776
|
+
|
|
80777
|
+
// src/tools/archive/tools.ts
|
|
80778
|
+
var ARCHIVE_READ_ANNOTATIONS = {
|
|
80779
|
+
readOnlyHint: true,
|
|
80780
|
+
destructiveHint: false,
|
|
80781
|
+
idempotentHint: true,
|
|
80782
|
+
openWorldHint: false
|
|
80783
|
+
};
|
|
80784
|
+
var PROVIDER_NAME = "Hive Archive";
|
|
80785
|
+
var SHARED_ARGS_DOC = `Args:
|
|
80786
|
+
- base_coin (string, required): base asset ticker, e.g. "BTC", "ETH", "SOL" (case-insensitive; uppercased server-side).
|
|
80787
|
+
- exchange (string, optional): one venue, matched case-insensitively (e.g. "Binance", "okx"). Omit for all venues.
|
|
80788
|
+
- from (string, optional): ISO 8601 inclusive lower bound.
|
|
80789
|
+
- to (string, optional): ISO 8601 inclusive upper bound.
|
|
80790
|
+
- limit (integer, optional): max rows (1-${MAX_LIMIT2}, default ${DEFAULT_LIMIT2}), newest first.`;
|
|
80791
|
+
var SHARED_RETURNS_DOC = `Bounded JSON envelope: { "provider", "dataset", "runtime_status", "fetched_at", "note", "filters", "order", "returnedCount", "may_have_more", "rows" }. Rows are newest-first; may_have_more true means the limit was hit \u2014 page older data by setting "to" below the oldest returned timestamp.`;
|
|
80792
|
+
function historyInputSchema(baseCoinDescription) {
|
|
80793
|
+
return {
|
|
80794
|
+
type: "object",
|
|
80795
|
+
properties: {
|
|
80796
|
+
base_coin: {
|
|
80797
|
+
type: "string",
|
|
80798
|
+
description: baseCoinDescription
|
|
80799
|
+
},
|
|
80800
|
+
exchange: {
|
|
80801
|
+
type: "string",
|
|
80802
|
+
description: 'Venue filter, case-insensitive exact match (e.g. "Binance", "Hyperliquid"). Call archive_get_coverage for the venue list.'
|
|
80803
|
+
},
|
|
80804
|
+
from: {
|
|
80805
|
+
type: "string",
|
|
80806
|
+
description: 'Inclusive ISO 8601 lower time bound, e.g. "2024-01-01T00:00:00Z".'
|
|
80807
|
+
},
|
|
80808
|
+
to: {
|
|
80809
|
+
type: "string",
|
|
80810
|
+
description: 'Inclusive ISO 8601 upper time bound, e.g. "2024-06-30T23:59:59Z".'
|
|
80811
|
+
},
|
|
80812
|
+
limit: {
|
|
80813
|
+
type: "integer",
|
|
80814
|
+
minimum: 1,
|
|
80815
|
+
maximum: MAX_LIMIT2,
|
|
80816
|
+
description: `Maximum rows to return (1-${MAX_LIMIT2}, default ${DEFAULT_LIMIT2}). Rows are ordered newest first.`
|
|
80817
|
+
}
|
|
80818
|
+
},
|
|
80819
|
+
required: ["base_coin"]
|
|
80820
|
+
};
|
|
80821
|
+
}
|
|
80822
|
+
function errorResult2(tool89, error) {
|
|
80823
|
+
if (error instanceof ArchiveInputError) {
|
|
80824
|
+
return asTextContentResult(
|
|
80825
|
+
{
|
|
80826
|
+
error: error.message,
|
|
80827
|
+
runtime_status: "invalid_input",
|
|
80828
|
+
tool: tool89
|
|
80829
|
+
},
|
|
80830
|
+
true
|
|
80831
|
+
);
|
|
80832
|
+
}
|
|
80833
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
80834
|
+
return asTextContentResult(
|
|
80835
|
+
{
|
|
80836
|
+
error: message,
|
|
80837
|
+
runtime_status: classifyRuntimeStatusFromError(message),
|
|
80838
|
+
tool: tool89
|
|
80839
|
+
},
|
|
80840
|
+
true
|
|
80841
|
+
);
|
|
80842
|
+
}
|
|
80843
|
+
function missingCredentialsResult(tool89) {
|
|
80844
|
+
return asTextContentResult(
|
|
80845
|
+
{
|
|
80846
|
+
error: ARCHIVE_MISSING_CREDENTIALS_MESSAGE,
|
|
80847
|
+
runtime_status: "missing_key",
|
|
80848
|
+
tool: tool89
|
|
80849
|
+
},
|
|
80850
|
+
true
|
|
80851
|
+
);
|
|
80852
|
+
}
|
|
80853
|
+
function historyHandler(toolName, dataset, options) {
|
|
80854
|
+
return async (_client, args) => {
|
|
80855
|
+
const supabase = getArchiveClient();
|
|
80856
|
+
if (!supabase) return missingCredentialsResult(toolName);
|
|
80857
|
+
try {
|
|
80858
|
+
const filters = parseHistoryArgs(args);
|
|
80859
|
+
const rows = await fetchHistoryRows(supabase, dataset, filters);
|
|
80860
|
+
const mapped = options?.mapRow ? rows.map(options.mapRow) : rows;
|
|
80861
|
+
const spec = HISTORY_TABLES[dataset];
|
|
80862
|
+
return asTextContentResult({
|
|
80863
|
+
provider: PROVIDER_NAME,
|
|
80864
|
+
dataset: spec.table,
|
|
80865
|
+
runtime_status: "ok",
|
|
80866
|
+
fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
80867
|
+
note: options?.extraNote ? `${ARCHIVE_SINCE_NOTE}. ${options.extraNote}` : ARCHIVE_SINCE_NOTE,
|
|
80868
|
+
filters,
|
|
80869
|
+
order: `${spec.tsColumn} desc`,
|
|
80870
|
+
returnedCount: mapped.length,
|
|
80871
|
+
may_have_more: mapped.length === filters.limit,
|
|
80872
|
+
rows: mapped
|
|
80873
|
+
});
|
|
80874
|
+
} catch (error) {
|
|
80875
|
+
return errorResult2(toolName, error);
|
|
80876
|
+
}
|
|
80877
|
+
};
|
|
80878
|
+
}
|
|
80879
|
+
function getAllArchiveEndpoints() {
|
|
80880
|
+
return [
|
|
80881
|
+
{
|
|
80882
|
+
metadata: {
|
|
80883
|
+
resource: "archive-funding-history",
|
|
80884
|
+
operation: "read",
|
|
80885
|
+
tags: ["archive", "derivatives", "funding", "history", "cross-venue"]
|
|
80886
|
+
},
|
|
80887
|
+
tool: {
|
|
80888
|
+
name: "archive_get_funding_history",
|
|
80889
|
+
description: `Get historical perp funding-rate SETTLEMENTS for one base coin across up to 33 CEX+DEX venues from the Hive Archive (${ARCHIVE_SINCE_NOTE}; 9.8M settlement rows). Each row is one settled funding event: exchange, symbol, base_coin, rate (raw per-interval fraction), interval_hours, settled_at \u2014 plus a derived funding_annualized computed honestly from that row's own interval (rate * 8760 / interval_hours; null when the interval is unknown, never a guessed 8h default).
|
|
80890
|
+
|
|
80891
|
+
${SHARED_ARGS_DOC}
|
|
80892
|
+
|
|
80893
|
+
Returns:
|
|
80894
|
+
${SHARED_RETURNS_DOC}
|
|
80895
|
+
|
|
80896
|
+
Examples:
|
|
80897
|
+
- { "base_coin": "BTC", "from": "2024-01-01T00:00:00Z", "limit": 200 } \u2014 BTC settlements across venues since 2024.
|
|
80898
|
+
- { "base_coin": "ETH", "exchange": "Hyperliquid", "limit": 100 } \u2014 recent ETH settlements on one venue.
|
|
80899
|
+
|
|
80900
|
+
Use when:
|
|
80901
|
+
- "BTC funding history across exchanges since 2024" \u2014 long-window, cross-venue funding studies
|
|
80902
|
+
- You are computing realized carry, funding regime shifts, or venue funding divergence over time
|
|
80903
|
+
- You need settlements older than what live venue APIs retain
|
|
80904
|
+
|
|
80905
|
+
Don't use when:
|
|
80906
|
+
- You need CURRENT funding (use ccxt_fetch_funding_rates_multi or hyperliquid_get_predicted_fundings)
|
|
80907
|
+
- You need one venue's own raw history endpoint (use get_funding_rate_history or hyperliquid_get_funding_history)
|
|
80908
|
+
- You need tokenized-stock/RWA perp funding (use rwa_get_funding_rates)`,
|
|
80909
|
+
inputSchema: historyInputSchema(
|
|
80910
|
+
'Base asset ticker to fetch settlements for, e.g. "BTC". Required \u2014 the archive is indexed by base coin.'
|
|
80911
|
+
),
|
|
80912
|
+
annotations: ARCHIVE_READ_ANNOTATIONS
|
|
80913
|
+
},
|
|
80914
|
+
handler: historyHandler("archive_get_funding_history", "funding", {
|
|
80915
|
+
mapRow: (row) => ({
|
|
80916
|
+
...row,
|
|
80917
|
+
funding_annualized: annualizeRate(
|
|
80918
|
+
Number(row.rate),
|
|
80919
|
+
Number(row.interval_hours)
|
|
80920
|
+
)
|
|
80921
|
+
})
|
|
80922
|
+
})
|
|
80923
|
+
},
|
|
80924
|
+
{
|
|
80925
|
+
metadata: {
|
|
80926
|
+
resource: "archive-open-interest-history",
|
|
80927
|
+
operation: "read",
|
|
80928
|
+
tags: [
|
|
80929
|
+
"archive",
|
|
80930
|
+
"derivatives",
|
|
80931
|
+
"open-interest",
|
|
80932
|
+
"history",
|
|
80933
|
+
"cross-venue"
|
|
80934
|
+
]
|
|
80935
|
+
},
|
|
80936
|
+
tool: {
|
|
80937
|
+
name: "archive_get_open_interest_history",
|
|
80938
|
+
description: `Get historical perp open-interest snapshots for one base coin across venues from the Hive Archive (2.1M rows). Each row: exchange, base_coin, symbol (venue-native contract), open_interest_value (venue-reported OI as collected), timestamp. Retention is shorter than funding \u2014 call archive_get_coverage for exact per-dataset depth.
|
|
80939
|
+
|
|
80940
|
+
${SHARED_ARGS_DOC}
|
|
80941
|
+
|
|
80942
|
+
Returns:
|
|
80943
|
+
${SHARED_RETURNS_DOC}
|
|
80944
|
+
|
|
80945
|
+
Examples:
|
|
80946
|
+
- { "base_coin": "ETH", "from": "2026-07-18T00:00:00Z", "to": "2026-08-18T00:00:00Z" } \u2014 ETH OI over the last month.
|
|
80947
|
+
- { "base_coin": "BTC", "exchange": "OKX", "limit": 500 } \u2014 recent BTC OI snapshots on OKX.
|
|
80948
|
+
|
|
80949
|
+
Use when:
|
|
80950
|
+
- "ETH open interest history last month" \u2014 OI trend and buildup/unwind analysis over time
|
|
80951
|
+
- You are correlating OI changes with funding or liquidation events across venues
|
|
80952
|
+
|
|
80953
|
+
Don't use when:
|
|
80954
|
+
- You need CURRENT open interest (use ccxt_fetch_open_interest_multi or get_open_interest)
|
|
80955
|
+
- You need one venue's own OI history endpoint shape (use get_open_interest_history via CCXT)`,
|
|
80956
|
+
inputSchema: historyInputSchema(
|
|
80957
|
+
'Base asset ticker to fetch open-interest snapshots for, e.g. "ETH". Required.'
|
|
80958
|
+
),
|
|
80959
|
+
annotations: ARCHIVE_READ_ANNOTATIONS
|
|
80960
|
+
},
|
|
80961
|
+
handler: historyHandler(
|
|
80962
|
+
"archive_get_open_interest_history",
|
|
80963
|
+
"open_interest",
|
|
80964
|
+
{
|
|
80965
|
+
extraNote: "open_interest_value is the venue-reported figure as collected (units are venue-native)."
|
|
80966
|
+
}
|
|
80967
|
+
)
|
|
80968
|
+
},
|
|
80969
|
+
{
|
|
80970
|
+
metadata: {
|
|
80971
|
+
resource: "archive-basis-history",
|
|
80972
|
+
operation: "read",
|
|
80973
|
+
tags: ["archive", "derivatives", "basis", "history", "cross-venue"]
|
|
80974
|
+
},
|
|
80975
|
+
tool: {
|
|
80976
|
+
name: "archive_get_basis_history",
|
|
80977
|
+
description: `Get historical spot-vs-futures BASIS snapshots for one base coin across venues from the Hive Archive. Each row: exchange, base_coin, spot_price, futures_price, basis_rate (fraction), annualized_basis, timestamp \u2014 the basis fields were computed at collection time from the venue's own spot and futures quotes.
|
|
80978
|
+
|
|
80979
|
+
${SHARED_ARGS_DOC}
|
|
80980
|
+
|
|
80981
|
+
Returns:
|
|
80982
|
+
${SHARED_RETURNS_DOC}
|
|
80983
|
+
|
|
80984
|
+
Examples:
|
|
80985
|
+
- { "base_coin": "BTC", "from": "2026-05-01T00:00:00Z", "limit": 300 } \u2014 BTC basis regime since May.
|
|
80986
|
+
- { "base_coin": "SOL", "exchange": "Binance" } \u2014 recent SOL basis snapshots on Binance.
|
|
80987
|
+
|
|
80988
|
+
Use when:
|
|
80989
|
+
- You are studying cash-and-carry yield history or basis blowouts/compressions over time
|
|
80990
|
+
- You need basis context around a historical funding or liquidation event
|
|
80991
|
+
|
|
80992
|
+
Don't use when:
|
|
80993
|
+
- You need LIVE basis or dated-futures term structure right now (use ccxt_ mark/index tools or hyperliquid tools)
|
|
80994
|
+
- You need current cross-venue funding instead of basis (use archive_get_funding_history for history, ccxt_fetch_funding_rates_multi for now)`,
|
|
80995
|
+
inputSchema: historyInputSchema(
|
|
80996
|
+
'Base asset ticker to fetch basis snapshots for, e.g. "BTC". Required.'
|
|
80997
|
+
),
|
|
80998
|
+
annotations: ARCHIVE_READ_ANNOTATIONS
|
|
80999
|
+
},
|
|
81000
|
+
handler: historyHandler("archive_get_basis_history", "basis")
|
|
81001
|
+
},
|
|
81002
|
+
{
|
|
81003
|
+
metadata: {
|
|
81004
|
+
resource: "archive-long-short-history",
|
|
81005
|
+
operation: "read",
|
|
81006
|
+
tags: [
|
|
81007
|
+
"archive",
|
|
81008
|
+
"derivatives",
|
|
81009
|
+
"long-short-ratio",
|
|
81010
|
+
"positioning",
|
|
81011
|
+
"history"
|
|
81012
|
+
]
|
|
81013
|
+
},
|
|
81014
|
+
tool: {
|
|
81015
|
+
name: "archive_get_long_short_history",
|
|
81016
|
+
description: `Get historical long/short-ratio snapshots for one base coin from the Hive Archive. Each row: exchange, base_coin, symbol, long_ratio, short_ratio (fractions summing to ~1), timestamp. The exchange column carries BOTH the all-account series (e.g. "Binance") and top-trader series (e.g. "Binance_top_trader") as separate venue values \u2014 filter with the exchange arg to pick one.
|
|
81017
|
+
|
|
81018
|
+
${SHARED_ARGS_DOC}
|
|
81019
|
+
|
|
81020
|
+
Returns:
|
|
81021
|
+
${SHARED_RETURNS_DOC}
|
|
81022
|
+
|
|
81023
|
+
Examples:
|
|
81024
|
+
- { "base_coin": "BTC", "exchange": "Binance_top_trader", "limit": 200 } \u2014 top-trader positioning history.
|
|
81025
|
+
- { "base_coin": "ETH", "from": "2026-06-01T00:00:00Z" } \u2014 all ETH positioning series since June.
|
|
81026
|
+
|
|
81027
|
+
Use when:
|
|
81028
|
+
- You are studying positioning shifts (crowded longs/shorts) around historical price moves
|
|
81029
|
+
- You need retail-vs-top-trader positioning divergence over time
|
|
81030
|
+
|
|
81031
|
+
Don't use when:
|
|
81032
|
+
- You need the CURRENT ratio (use get_long_short_ratio or ccxt_fetch_long_short_ratio_history for venue-native reads)
|
|
81033
|
+
- You need funding or OI history (use the dedicated archive_ tools)`,
|
|
81034
|
+
inputSchema: historyInputSchema(
|
|
81035
|
+
'Base asset ticker to fetch long/short snapshots for, e.g. "BTC". Required.'
|
|
81036
|
+
),
|
|
81037
|
+
annotations: ARCHIVE_READ_ANNOTATIONS
|
|
81038
|
+
},
|
|
81039
|
+
handler: historyHandler("archive_get_long_short_history", "long_short")
|
|
81040
|
+
},
|
|
81041
|
+
{
|
|
81042
|
+
metadata: {
|
|
81043
|
+
resource: "archive-liquidations-history",
|
|
81044
|
+
operation: "read",
|
|
81045
|
+
tags: ["archive", "derivatives", "liquidations", "history"]
|
|
81046
|
+
},
|
|
81047
|
+
tool: {
|
|
81048
|
+
name: "archive_get_liquidations_history",
|
|
81049
|
+
description: `Get historical liquidation events for one base coin from the Hive Archive. Each row: exchange, base_coin, symbol, side ("long" = longs liquidated, "short" = shorts liquidated), quantity, price, timestamp. quantity and price are the venue-reported values as collected \u2014 units are venue-native, so compare quantities only within one exchange.
|
|
81050
|
+
|
|
81051
|
+
${SHARED_ARGS_DOC}
|
|
81052
|
+
|
|
81053
|
+
Returns:
|
|
81054
|
+
${SHARED_RETURNS_DOC}
|
|
81055
|
+
|
|
81056
|
+
Examples:
|
|
81057
|
+
- { "base_coin": "BTC", "from": "2026-08-01T00:00:00Z", "limit": 500 } \u2014 August BTC liquidation prints.
|
|
81058
|
+
- { "base_coin": "ETH", "exchange": "Binance", "limit": 100 } \u2014 recent ETH liquidations on Binance.
|
|
81059
|
+
|
|
81060
|
+
Use when:
|
|
81061
|
+
- You are reconstructing a liquidation cascade or mapping historical liquidation clusters by price
|
|
81062
|
+
- You need liquidation context around a historical funding/OI/basis move
|
|
81063
|
+
|
|
81064
|
+
Don't use when:
|
|
81065
|
+
- You need LIVE liquidations (use get_liquidations via CCXT)
|
|
81066
|
+
- You need aggregate liquidation totals per venue right now (live venue tools)`,
|
|
81067
|
+
inputSchema: historyInputSchema(
|
|
81068
|
+
'Base asset ticker to fetch liquidation events for, e.g. "BTC". Required.'
|
|
81069
|
+
),
|
|
81070
|
+
annotations: ARCHIVE_READ_ANNOTATIONS
|
|
81071
|
+
},
|
|
81072
|
+
handler: historyHandler(
|
|
81073
|
+
"archive_get_liquidations_history",
|
|
81074
|
+
"liquidations"
|
|
81075
|
+
)
|
|
81076
|
+
},
|
|
81077
|
+
{
|
|
81078
|
+
metadata: {
|
|
81079
|
+
resource: "archive-coverage",
|
|
81080
|
+
operation: "read",
|
|
81081
|
+
tags: ["archive", "derivatives", "coverage", "discovery"]
|
|
81082
|
+
},
|
|
81083
|
+
tool: {
|
|
81084
|
+
name: "archive_get_coverage",
|
|
81085
|
+
description: `Discover what the Hive Archive holds: the all-time venue list (33 CEX+DEX funding venues) and earliest/latest timestamps per dataset (funding settlements, open interest, basis, long/short ratio, liquidations). Funding extremes are global; futures-dataset extremes are probed for one base coin (default "BTC" \u2014 verified to match the global extremes) because those tables are indexed by (base_coin, timestamp). Results are cached in-process for 10 minutes.
|
|
81086
|
+
|
|
81087
|
+
Args:
|
|
81088
|
+
- base_coin (string, optional): probe coin for the futures datasets (default "BTC"). Pass another ticker to check archive depth for that asset.
|
|
81089
|
+
|
|
81090
|
+
Returns:
|
|
81091
|
+
JSON object: { "provider", "runtime_status", "fetched_at", "cache", "venues": [...], "venue_count", "venues_truncated", "probe_base_coin", "datasets": [{ "dataset", "table", "scope", "earliest_at", "latest_at" }], "note" }.
|
|
81092
|
+
|
|
81093
|
+
Examples:
|
|
81094
|
+
- {} \u2014 full coverage picture with BTC probes.
|
|
81095
|
+
- { "base_coin": "DOGE" } \u2014 how far back DOGE futures datasets go.
|
|
81096
|
+
|
|
81097
|
+
Use when:
|
|
81098
|
+
- Before a history query, to learn venue names and how far back each dataset reaches
|
|
81099
|
+
- "Which exchanges does the funding archive cover?" / "since when is there OI history?"
|
|
81100
|
+
|
|
81101
|
+
Don't use when:
|
|
81102
|
+
- You already know the venue and range (call the history tool directly)
|
|
81103
|
+
- You need live venue universes (use ccxt_fetch_markets or venue tools)`,
|
|
81104
|
+
inputSchema: {
|
|
81105
|
+
type: "object",
|
|
81106
|
+
properties: {
|
|
81107
|
+
base_coin: {
|
|
81108
|
+
type: "string",
|
|
81109
|
+
description: 'Probe coin for futures-dataset extremes (default "BTC").'
|
|
81110
|
+
}
|
|
81111
|
+
}
|
|
81112
|
+
},
|
|
81113
|
+
annotations: ARCHIVE_READ_ANNOTATIONS
|
|
81114
|
+
},
|
|
81115
|
+
handler: async (_client, args) => {
|
|
81116
|
+
const toolName = "archive_get_coverage";
|
|
81117
|
+
const supabase = getArchiveClient();
|
|
81118
|
+
if (!supabase) return missingCredentialsResult(toolName);
|
|
81119
|
+
try {
|
|
81120
|
+
const probe = typeof args?.base_coin === "string" && args.base_coin.trim() !== "" ? args.base_coin.trim().toUpperCase() : "BTC";
|
|
81121
|
+
const { coverage, cache } = await fetchCoverage(supabase, probe);
|
|
81122
|
+
return asTextContentResult({
|
|
81123
|
+
provider: PROVIDER_NAME,
|
|
81124
|
+
runtime_status: "ok",
|
|
81125
|
+
fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
81126
|
+
cache,
|
|
81127
|
+
...coverage
|
|
81128
|
+
});
|
|
81129
|
+
} catch (error) {
|
|
81130
|
+
return errorResult2(toolName, error);
|
|
81131
|
+
}
|
|
81132
|
+
}
|
|
81133
|
+
}
|
|
81134
|
+
];
|
|
81135
|
+
}
|
|
81136
|
+
|
|
80543
81137
|
// src/tools/fetch/allowlist.ts
|
|
80544
81138
|
var KB = 1024;
|
|
80545
81139
|
var FETCH_ALLOWLIST = [
|
|
@@ -81131,9 +81725,16 @@ function generateStructuredDescription(toolName, existingDescription, inputSchem
|
|
|
81131
81725
|
}
|
|
81132
81726
|
function buildSummary(toolName, existing) {
|
|
81133
81727
|
let summary = existing.replace(/\*\*/g, "").trim();
|
|
81134
|
-
|
|
81135
|
-
|
|
81136
|
-
|
|
81728
|
+
const boilerplate = /^this endpoint (?:allows you to|lets you|is used to|can be used to)\s+/i;
|
|
81729
|
+
if (boilerplate.test(summary)) {
|
|
81730
|
+
summary = summary.replace(boilerplate, "");
|
|
81731
|
+
summary = summary.charAt(0).toUpperCase() + summary.slice(1);
|
|
81732
|
+
} else if (
|
|
81733
|
+
// Ensure it starts with a verb
|
|
81734
|
+
!/^(Get|Fetch|Retrieve|Search|Filter|Check|List|Return|Query)/i.test(
|
|
81735
|
+
summary
|
|
81736
|
+
)
|
|
81737
|
+
) {
|
|
81137
81738
|
const verb = inferVerb(toolName);
|
|
81138
81739
|
summary = `${verb} ${summary.charAt(0).toLowerCase()}${summary.slice(1)}`;
|
|
81139
81740
|
}
|
|
@@ -81843,9 +82444,90 @@ function getOutputSchemaForTool(toolName) {
|
|
|
81843
82444
|
}
|
|
81844
82445
|
return GENERIC_TOOL_OUTPUT_SCHEMA;
|
|
81845
82446
|
}
|
|
82447
|
+
var SOURCE_RECEIPT_SCHEMA = {
|
|
82448
|
+
type: "object",
|
|
82449
|
+
additionalProperties: true,
|
|
82450
|
+
required: ["provider", "runtime_status", "fetched_at"],
|
|
82451
|
+
properties: {
|
|
82452
|
+
provider: { type: "string" },
|
|
82453
|
+
tool: { type: "string" },
|
|
82454
|
+
runtime_status: { type: "string", enum: [...RUNTIME_STATUSES] },
|
|
82455
|
+
fetched_at: { type: "string", format: "date-time" },
|
|
82456
|
+
cache_status: { type: "string" },
|
|
82457
|
+
source: { type: "string" },
|
|
82458
|
+
receipt_id: { type: "string" }
|
|
82459
|
+
},
|
|
82460
|
+
description: "Provenance for this observation: which upstream served it, its runtime status, and when it was fetched."
|
|
82461
|
+
};
|
|
82462
|
+
var RECEIPT_REF = {
|
|
82463
|
+
type: "object",
|
|
82464
|
+
additionalProperties: true,
|
|
82465
|
+
required: ["provider", "runtime_status", "fetched_at"],
|
|
82466
|
+
properties: {
|
|
82467
|
+
provider: { type: "string" },
|
|
82468
|
+
runtime_status: { type: "string", enum: [...RUNTIME_STATUSES] },
|
|
82469
|
+
fetched_at: { type: "string", format: "date-time" }
|
|
82470
|
+
}
|
|
82471
|
+
};
|
|
82472
|
+
var GET_TOKEN_PRICE_OUTPUT_SCHEMA = {
|
|
82473
|
+
type: "object",
|
|
82474
|
+
additionalProperties: true,
|
|
82475
|
+
required: ["source_receipt"],
|
|
82476
|
+
properties: {
|
|
82477
|
+
source_receipt: RECEIPT_REF,
|
|
82478
|
+
token: { type: "string" },
|
|
82479
|
+
vs_currency: { type: "string" },
|
|
82480
|
+
price: { type: "number" },
|
|
82481
|
+
market_cap: { type: "number" },
|
|
82482
|
+
change_24h_percent: { type: "number" }
|
|
82483
|
+
}
|
|
82484
|
+
};
|
|
82485
|
+
var CHECK_TOKEN_SAFETY_OUTPUT_SCHEMA = {
|
|
82486
|
+
type: "object",
|
|
82487
|
+
additionalProperties: true,
|
|
82488
|
+
required: ["source_receipt"],
|
|
82489
|
+
properties: {
|
|
82490
|
+
source_receipt: RECEIPT_REF,
|
|
82491
|
+
address: { type: "string" },
|
|
82492
|
+
chain: { type: "string" },
|
|
82493
|
+
risk_flags: {
|
|
82494
|
+
type: "object",
|
|
82495
|
+
additionalProperties: true,
|
|
82496
|
+
description: "Raw provider risk datapoints; '1' means flagged. A missing key means the provider did not report that signal, not that it passed."
|
|
82497
|
+
},
|
|
82498
|
+
holder_count: {},
|
|
82499
|
+
token_name: { type: "string" },
|
|
82500
|
+
note: { type: "string" }
|
|
82501
|
+
}
|
|
82502
|
+
};
|
|
82503
|
+
var GET_WALLET_PORTFOLIO_OUTPUT_SCHEMA = {
|
|
82504
|
+
type: "object",
|
|
82505
|
+
additionalProperties: true,
|
|
82506
|
+
required: ["source_receipt"],
|
|
82507
|
+
properties: {
|
|
82508
|
+
source_receipt: RECEIPT_REF,
|
|
82509
|
+
address: { type: "string" },
|
|
82510
|
+
chain: { type: "string" },
|
|
82511
|
+
token_count: {
|
|
82512
|
+
type: "number",
|
|
82513
|
+
description: "Total holdings found, which may exceed tokens.length."
|
|
82514
|
+
},
|
|
82515
|
+
tokens: {
|
|
82516
|
+
type: "array",
|
|
82517
|
+
items: { type: "object", additionalProperties: true },
|
|
82518
|
+
description: "Holdings; truncated in concise mode when note is present."
|
|
82519
|
+
},
|
|
82520
|
+
note: { type: "string" }
|
|
82521
|
+
}
|
|
82522
|
+
};
|
|
82523
|
+
var HERO_TOOL_OUTPUT_SCHEMAS = {
|
|
82524
|
+
get_token_price: GET_TOKEN_PRICE_OUTPUT_SCHEMA,
|
|
82525
|
+
check_token_safety: CHECK_TOKEN_SAFETY_OUTPUT_SCHEMA,
|
|
82526
|
+
get_wallet_portfolio: GET_WALLET_PORTFOLIO_OUTPUT_SCHEMA
|
|
82527
|
+
};
|
|
81846
82528
|
|
|
81847
82529
|
// src/utils/analytics.ts
|
|
81848
|
-
import { randomUUID } from "crypto";
|
|
82530
|
+
import { createHash as createHash3, randomUUID } from "crypto";
|
|
81849
82531
|
var GA_ENDPOINT = "https://www.google-analytics.com/mp/collect";
|
|
81850
82532
|
function getMeasurementId() {
|
|
81851
82533
|
return getEnvVar("GA_MEASUREMENT_ID");
|
|
@@ -81876,7 +82558,9 @@ async function sendEvents(clientId, events) {
|
|
|
81876
82558
|
}
|
|
81877
82559
|
}
|
|
81878
82560
|
function deriveClientId(ip, userAgent, userId) {
|
|
81879
|
-
if (userId)
|
|
82561
|
+
if (userId) {
|
|
82562
|
+
return `u_${createHash3("sha256").update(userId).digest("hex").slice(0, 32)}`;
|
|
82563
|
+
}
|
|
81880
82564
|
const raw = `${ip || "unknown"}_${userAgent || "unknown"}`;
|
|
81881
82565
|
let hash = 0;
|
|
81882
82566
|
for (let i = 0; i < raw.length; i++) {
|
|
@@ -82020,24 +82704,32 @@ async function closeRedisClient() {
|
|
|
82020
82704
|
current.disconnect();
|
|
82021
82705
|
}
|
|
82022
82706
|
}
|
|
82707
|
+
var DEGRADED_AFTER_ATTEMPTS = 3;
|
|
82708
|
+
var MAX_RECONNECT_DELAY_MS = 3e3;
|
|
82709
|
+
function redisRetryStrategy(times) {
|
|
82710
|
+
if (times === DEGRADED_AFTER_ATTEMPTS) {
|
|
82711
|
+
secureLogger.warn(
|
|
82712
|
+
`[redis] Connection failed after ${DEGRADED_AFTER_ATTEMPTS} retries, falling back to in-memory while reconnection continues`,
|
|
82713
|
+
{}
|
|
82714
|
+
);
|
|
82715
|
+
available = false;
|
|
82716
|
+
}
|
|
82717
|
+
return Math.min(times * 100, MAX_RECONNECT_DELAY_MS);
|
|
82718
|
+
}
|
|
82719
|
+
function redisClientOptions() {
|
|
82720
|
+
return {
|
|
82721
|
+
maxRetriesPerRequest: 3,
|
|
82722
|
+
retryStrategy: redisRetryStrategy,
|
|
82723
|
+
lazyConnect: true
|
|
82724
|
+
};
|
|
82725
|
+
}
|
|
82023
82726
|
var redisUrl = getEnvVar("REDIS_URL");
|
|
82727
|
+
function isRedisConfigured() {
|
|
82728
|
+
return Boolean(redisUrl);
|
|
82729
|
+
}
|
|
82024
82730
|
if (redisUrl) {
|
|
82025
82731
|
try {
|
|
82026
|
-
client = new Redis(redisUrl,
|
|
82027
|
-
maxRetriesPerRequest: 3,
|
|
82028
|
-
retryStrategy: (times) => {
|
|
82029
|
-
if (times > 3) {
|
|
82030
|
-
secureLogger.warn(
|
|
82031
|
-
"[redis] Connection failed after 3 retries, falling back to in-memory",
|
|
82032
|
-
{}
|
|
82033
|
-
);
|
|
82034
|
-
available = false;
|
|
82035
|
-
return null;
|
|
82036
|
-
}
|
|
82037
|
-
return Math.min(times * 100, 1e3);
|
|
82038
|
-
},
|
|
82039
|
-
lazyConnect: true
|
|
82040
|
-
});
|
|
82732
|
+
client = new Redis(redisUrl, redisClientOptions());
|
|
82041
82733
|
client.connect().then(() => {
|
|
82042
82734
|
available = true;
|
|
82043
82735
|
logRedisInfo("[redis] Connected successfully");
|
|
@@ -82313,6 +83005,24 @@ var PROVIDER_CATALOG = [
|
|
|
82313
83005
|
alwaysAvailable: true,
|
|
82314
83006
|
matches: (toolName, metadata93) => matchesProvider("hyperliquid", toolName, metadata93)
|
|
82315
83007
|
},
|
|
83008
|
+
{
|
|
83009
|
+
// The Hive-native hive_archive_monitor tool contains the "_archive_"
|
|
83010
|
+
// infix this matcher would accept; the "hive" entry is listed first and
|
|
83011
|
+
// catalog order decides classification, so hive_ tools never land here.
|
|
83012
|
+
id: "archive",
|
|
83013
|
+
name: "Hive Archive",
|
|
83014
|
+
prefix: "archive_",
|
|
83015
|
+
type: "Cross-venue crypto derivatives history since 2023-11 (33 CEX+DEX venues): funding-rate settlements, open interest, basis, long/short ratio, and liquidations from Hive's own archive store",
|
|
83016
|
+
clientStrategy: "internal",
|
|
83017
|
+
credentialRequirement: {
|
|
83018
|
+
label: "SHARPE_ARCHIVE_SUPABASE_URL and SHARPE_ARCHIVE_SUPABASE_SERVICE_ROLE_KEY",
|
|
83019
|
+
isConfigured: () => hasAllEnvVars(
|
|
83020
|
+
"SHARPE_ARCHIVE_SUPABASE_URL",
|
|
83021
|
+
"SHARPE_ARCHIVE_SUPABASE_SERVICE_ROLE_KEY"
|
|
83022
|
+
)
|
|
83023
|
+
},
|
|
83024
|
+
matches: (toolName, metadata93) => matchesProvider("archive", toolName, metadata93)
|
|
83025
|
+
},
|
|
82316
83026
|
{
|
|
82317
83027
|
id: "fetch",
|
|
82318
83028
|
name: "Open Data Fetch",
|
|
@@ -83089,6 +83799,7 @@ function getAllWrappedEndpoints() {
|
|
|
83089
83799
|
...getAllAlchemyEndpoints(),
|
|
83090
83800
|
...getAllHyperliquidEndpoints(),
|
|
83091
83801
|
...getAllRwaEndpoints(),
|
|
83802
|
+
...getAllArchiveEndpoints(),
|
|
83092
83803
|
...getAllFetchEndpoints()
|
|
83093
83804
|
];
|
|
83094
83805
|
const allEndpointsJSON = wrapper;
|
|
@@ -83163,6 +83874,7 @@ function getAllWrappedEndpoints() {
|
|
|
83163
83874
|
"hive_",
|
|
83164
83875
|
"hyperliquid_",
|
|
83165
83876
|
"rwa_",
|
|
83877
|
+
"archive_",
|
|
83166
83878
|
"fetch_"
|
|
83167
83879
|
];
|
|
83168
83880
|
if (selfDescribedPrefixes.some((p) => tool89.name.startsWith(p))) {
|
|
@@ -83222,7 +83934,7 @@ var ToolRegistry = [
|
|
|
83222
83934
|
{
|
|
83223
83935
|
category: "Market Data and Price",
|
|
83224
83936
|
name: "get_market_and_price_endpoints",
|
|
83225
|
-
description: "Endpoints to retrieve real-time and historical cryptocurrency prices, market caps, trading volumes, OHLC data, global market statistics, supported currencies, basic market performance metrics across different timeframes and asset platforms, stablecoin market analytics, stablecoin price tracking, comprehensive stablecoin market cap data across multiple chains, centralized exchange (CEX) trading data including real-time tickers, order books, recent trades, candlestick charts, best bid/ask prices, CEX exchange metadata and volume charts, derivatives pricing (index, mark, and premium), perpetual futures funding rates, leverage tiers, options trading analytics, and available trading markets/currencies across major exchanges. Provider guide: Use coingecko tools for market-wide price data. Use ccxt_ tools for CEX trading data. Use alchemy_ tools for supported token price reads and EVM gas data. Use codex tools for on-chain DEX token price, chart, and volume analytics.",
|
|
83937
|
+
description: "Endpoints to retrieve real-time and historical cryptocurrency prices, market caps, trading volumes, OHLC data, global market statistics, supported currencies, basic market performance metrics across different timeframes and asset platforms, stablecoin market analytics, stablecoin price tracking, comprehensive stablecoin market cap data across multiple chains, centralized exchange (CEX) trading data including real-time tickers, order books, recent trades, candlestick charts, best bid/ask prices, CEX exchange metadata and volume charts, derivatives pricing (index, mark, and premium), perpetual futures funding rates, leverage tiers, options trading analytics, and available trading markets/currencies across major exchanges. Provider guide: Use coingecko tools for market-wide price data. Use ccxt_ tools for CEX trading data. Use alchemy_ tools for supported token price reads and EVM gas data. Use codex tools for on-chain DEX token price, chart, and volume analytics. Use archive_ tools for cross-venue derivatives HISTORY (funding settlements, open interest, basis, long/short ratio, liquidations) since 2023-11.",
|
|
83226
83938
|
tools: [
|
|
83227
83939
|
"get_coins_index",
|
|
83228
83940
|
"get_coins_market_data",
|
|
@@ -83289,6 +84001,15 @@ var ToolRegistry = [
|
|
|
83289
84001
|
"rwa_get_avantis_markets",
|
|
83290
84002
|
"rwa_get_lighter_markets",
|
|
83291
84003
|
"rwa_get_extended_markets",
|
|
84004
|
+
// Hive Archive cross-venue derivatives history (since 2023-11).
|
|
84005
|
+
// Market data by fit, same reasoning as the RWA slice — the registry
|
|
84006
|
+
// stays at 10 categories (no 11th category surface).
|
|
84007
|
+
"archive_get_funding_history",
|
|
84008
|
+
"archive_get_open_interest_history",
|
|
84009
|
+
"archive_get_basis_history",
|
|
84010
|
+
"archive_get_long_short_history",
|
|
84011
|
+
"archive_get_liquidations_history",
|
|
84012
|
+
"archive_get_coverage",
|
|
83292
84013
|
"get_stablecoins",
|
|
83293
84014
|
"get_stablecoin_chains",
|
|
83294
84015
|
"get_stablecoin_charts_global",
|
|
@@ -83948,7 +84669,7 @@ import { z as z15 } from "zod";
|
|
|
83948
84669
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
83949
84670
|
|
|
83950
84671
|
// packages/mcp-client/src/receipt-integrity.ts
|
|
83951
|
-
import { createHash as
|
|
84672
|
+
import { createHash as createHash4 } from "crypto";
|
|
83952
84673
|
function compareJsonKeys(left, right) {
|
|
83953
84674
|
if (left === right) return 0;
|
|
83954
84675
|
return left < right ? -1 : 1;
|
|
@@ -83973,7 +84694,7 @@ function canonicalizeHiveJson(value) {
|
|
|
83973
84694
|
}
|
|
83974
84695
|
}
|
|
83975
84696
|
function digestHiveCanonicalJson(value) {
|
|
83976
|
-
return
|
|
84697
|
+
return createHash4("sha256").update(canonicalizeHiveJson(value)).digest("hex");
|
|
83977
84698
|
}
|
|
83978
84699
|
|
|
83979
84700
|
// src/utils/jsonSchemaValidator.ts
|
|
@@ -84785,6 +85506,7 @@ function getMcpCreditCost(toolName) {
|
|
|
84785
85506
|
// src/services/analytics-writer.ts
|
|
84786
85507
|
import { createHmac as createHmac2 } from "crypto";
|
|
84787
85508
|
var PRINCIPAL_HASH_DOMAIN = "hive-analytics-principal-v1";
|
|
85509
|
+
var DROP_LOG_INTERVAL_MS = 6e4;
|
|
84788
85510
|
var MAX_QUEUE_SIZE = 500;
|
|
84789
85511
|
var FLUSH_INTERVAL_MS = 5e3;
|
|
84790
85512
|
var FLUSH_BATCH_SIZE = 100;
|
|
@@ -84817,6 +85539,7 @@ function createAnalyticsWriter(overrides = {}) {
|
|
|
84817
85539
|
};
|
|
84818
85540
|
const queue = [];
|
|
84819
85541
|
let dropped = 0;
|
|
85542
|
+
let lastDropLoggedAt = 0;
|
|
84820
85543
|
let timer = null;
|
|
84821
85544
|
let flushing = null;
|
|
84822
85545
|
function hashPrincipal(raw) {
|
|
@@ -84876,6 +85599,14 @@ function createAnalyticsWriter(overrides = {}) {
|
|
|
84876
85599
|
if (!deps.isConfigured()) return false;
|
|
84877
85600
|
if (queue.length >= deps.maxQueueSize) {
|
|
84878
85601
|
dropped += 1;
|
|
85602
|
+
const now = Date.now();
|
|
85603
|
+
if (now - lastDropLoggedAt >= DROP_LOG_INTERVAL_MS) {
|
|
85604
|
+
lastDropLoggedAt = now;
|
|
85605
|
+
secureLogger.warn(
|
|
85606
|
+
"Analytics queue is full \u2014 product-analytics rows are being dropped",
|
|
85607
|
+
{ table, queueSize: queue.length, droppedTotal: dropped }
|
|
85608
|
+
);
|
|
85609
|
+
}
|
|
84879
85610
|
return false;
|
|
84880
85611
|
}
|
|
84881
85612
|
queue.push({ table, row });
|
|
@@ -85771,7 +86502,7 @@ function isSolanaChain(chain, address) {
|
|
|
85771
86502
|
if (chain) return SOLANA_CHAIN_NAMES.has(normalizeChain(chain));
|
|
85772
86503
|
return !address.trim().toLowerCase().startsWith("0x");
|
|
85773
86504
|
}
|
|
85774
|
-
function
|
|
86505
|
+
function errorResult3(message) {
|
|
85775
86506
|
return {
|
|
85776
86507
|
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
85777
86508
|
isError: true
|
|
@@ -85805,7 +86536,7 @@ function heroTools(endpoints, options = {}) {
|
|
|
85805
86536
|
const executeUnderlying = async (toolName, args) => {
|
|
85806
86537
|
const lookup = resolveEndpoint(endpoints, toolName);
|
|
85807
86538
|
if (!lookup.ok) {
|
|
85808
|
-
return
|
|
86539
|
+
return errorResult3(
|
|
85809
86540
|
`Underlying endpoint '${toolName}' is not registered. Call search_tools to locate the current tool for this intent.`
|
|
85810
86541
|
);
|
|
85811
86542
|
}
|
|
@@ -85890,12 +86621,13 @@ function heroTools(endpoints, options = {}) {
|
|
|
85890
86621
|
title: "Get Token Price",
|
|
85891
86622
|
description: 'Current price for one token. Pass token as a CoinGecko id or symbol ("bitcoin", "BTC"), or pass chain + address for a specific contract. Returns price, market cap, and 24h change with provider provenance.',
|
|
85892
86623
|
inputSchema: zodToInputSchema2(tokenPriceSchema),
|
|
86624
|
+
outputSchema: heroOutputSchema("get_token_price"),
|
|
85893
86625
|
annotations: VOLATILE_READ_ANNOTATIONS
|
|
85894
86626
|
},
|
|
85895
86627
|
handler: async (_client, args) => {
|
|
85896
86628
|
const parsed = tokenPriceSchema.safeParse(args ?? {});
|
|
85897
86629
|
if (!parsed.success) {
|
|
85898
|
-
return
|
|
86630
|
+
return errorResult3(
|
|
85899
86631
|
`Invalid arguments: ${JSON.stringify(parsed.error.format())}`
|
|
85900
86632
|
);
|
|
85901
86633
|
}
|
|
@@ -85911,7 +86643,7 @@ function heroTools(endpoints, options = {}) {
|
|
|
85911
86643
|
return respond(result3, format, trimOnchainPrice(address));
|
|
85912
86644
|
}
|
|
85913
86645
|
if (!token) {
|
|
85914
|
-
return
|
|
86646
|
+
return errorResult3(
|
|
85915
86647
|
'Pass token (CoinGecko id or symbol, for example "bitcoin" or "BTC"), or chain + address for a contract.'
|
|
85916
86648
|
);
|
|
85917
86649
|
}
|
|
@@ -85990,12 +86722,13 @@ function heroTools(endpoints, options = {}) {
|
|
|
85990
86722
|
title: "Check Token Safety",
|
|
85991
86723
|
description: "Token security screen for a contract address: honeypot, taxes, ownership, and related GoPlus risk datapoints. EVM chains use chain + address; Solana addresses are detected automatically.",
|
|
85992
86724
|
inputSchema: zodToInputSchema2(tokenSafetySchema),
|
|
86725
|
+
outputSchema: heroOutputSchema("check_token_safety"),
|
|
85993
86726
|
annotations: VOLATILE_READ_ANNOTATIONS
|
|
85994
86727
|
},
|
|
85995
86728
|
handler: async (_client, args) => {
|
|
85996
86729
|
const parsed = tokenSafetySchema.safeParse(args ?? {});
|
|
85997
86730
|
if (!parsed.success) {
|
|
85998
|
-
return
|
|
86731
|
+
return errorResult3(
|
|
85999
86732
|
`Invalid arguments: ${JSON.stringify(parsed.error.format())}`
|
|
86000
86733
|
);
|
|
86001
86734
|
}
|
|
@@ -86010,7 +86743,7 @@ function heroTools(endpoints, options = {}) {
|
|
|
86010
86743
|
const normalized = normalizeChain(chain);
|
|
86011
86744
|
const chainId = /^\d+$/.test(normalized) ? normalized : GOPLUS_CHAIN_IDS[normalized];
|
|
86012
86745
|
if (!chainId) {
|
|
86013
|
-
return
|
|
86746
|
+
return errorResult3(
|
|
86014
86747
|
`Unknown chain '${chain}'. Pass an EVM chain name (${Object.keys(GOPLUS_CHAIN_IDS).slice(0, 6).join(", ")}, \u2026), a numeric chain id, or "solana".`
|
|
86015
86748
|
);
|
|
86016
86749
|
}
|
|
@@ -86029,6 +86762,54 @@ function heroTools(endpoints, options = {}) {
|
|
|
86029
86762
|
response_format: RESPONSE_FORMAT
|
|
86030
86763
|
});
|
|
86031
86764
|
const MAX_CONCISE_TOKENS = 15;
|
|
86765
|
+
const NATIVE_SYMBOLS = {
|
|
86766
|
+
eth: "ETH",
|
|
86767
|
+
ethereum: "ETH",
|
|
86768
|
+
mainnet: "ETH",
|
|
86769
|
+
base: "ETH",
|
|
86770
|
+
polygon: "POL",
|
|
86771
|
+
matic: "POL",
|
|
86772
|
+
arbitrum: "ETH",
|
|
86773
|
+
arb: "ETH",
|
|
86774
|
+
optimism: "ETH",
|
|
86775
|
+
opt: "ETH",
|
|
86776
|
+
bsc: "BNB",
|
|
86777
|
+
bnb: "BNB",
|
|
86778
|
+
avalanche: "AVAX",
|
|
86779
|
+
avax: "AVAX",
|
|
86780
|
+
solana: "SOL"
|
|
86781
|
+
};
|
|
86782
|
+
const decodeBalance = (raw, decimals) => {
|
|
86783
|
+
if (raw == null) return null;
|
|
86784
|
+
if (typeof raw === "number")
|
|
86785
|
+
return Number.isFinite(raw) ? String(raw) : null;
|
|
86786
|
+
if (typeof raw !== "string") return null;
|
|
86787
|
+
const text = raw.trim();
|
|
86788
|
+
if (!text) return null;
|
|
86789
|
+
let units;
|
|
86790
|
+
try {
|
|
86791
|
+
units = BigInt(text);
|
|
86792
|
+
} catch {
|
|
86793
|
+
return text;
|
|
86794
|
+
}
|
|
86795
|
+
const scale = typeof decimals === "number" && Number.isInteger(decimals) && decimals > 0 ? decimals : 0;
|
|
86796
|
+
if (scale === 0) return units.toString();
|
|
86797
|
+
const base = 10n ** BigInt(scale);
|
|
86798
|
+
const whole = units / base;
|
|
86799
|
+
const frac = (units % base).toString().padStart(scale, "0").replace(/0+$/, "");
|
|
86800
|
+
return frac ? `${whole}.${frac.slice(0, 8)}` : whole.toString();
|
|
86801
|
+
};
|
|
86802
|
+
const usdFromPrices = (token, balance) => {
|
|
86803
|
+
if (!Array.isArray(token.tokenPrices)) return null;
|
|
86804
|
+
const usd = token.tokenPrices.find(
|
|
86805
|
+
(price) => isRecord3(price) && price.currency === "usd"
|
|
86806
|
+
);
|
|
86807
|
+
const rate = usd && isRecord3(usd) ? Number(usd.value) : NaN;
|
|
86808
|
+
const quantity = balance == null ? NaN : Number(balance);
|
|
86809
|
+
const value = rate * quantity;
|
|
86810
|
+
if (!Number.isFinite(value)) return null;
|
|
86811
|
+
return value >= 0.01 ? Math.round(value * 100) / 100 : value;
|
|
86812
|
+
};
|
|
86032
86813
|
const firstArray = (payload, keys) => {
|
|
86033
86814
|
for (const key of keys) {
|
|
86034
86815
|
const direct = payload[key];
|
|
@@ -86041,25 +86822,40 @@ function heroTools(endpoints, options = {}) {
|
|
|
86041
86822
|
return [];
|
|
86042
86823
|
};
|
|
86043
86824
|
const trimWalletPortfolio = (address, chainLabel) => (payload) => {
|
|
86825
|
+
const walletLower = address.trim().toLowerCase();
|
|
86044
86826
|
const tokens = firstArray(payload, ["tokens", "items", "balances"]);
|
|
86045
|
-
const summarized = tokens.
|
|
86827
|
+
const summarized = tokens.map((token) => {
|
|
86046
86828
|
if (!isRecord3(token)) return token;
|
|
86047
86829
|
const metadata93 = isRecord3(token.tokenMetadata) ? token.tokenMetadata : isRecord3(token.token) ? token.token : void 0;
|
|
86830
|
+
const fallbackAddress = typeof token.address === "string" && token.address.trim().toLowerCase() === walletLower ? null : token.address ?? null;
|
|
86831
|
+
const tokenAddress = token.tokenAddress ?? token.mint ?? fallbackAddress;
|
|
86832
|
+
const isNative = tokenAddress == null;
|
|
86833
|
+
const rawDecimals = token.decimals ?? metadata93?.decimals;
|
|
86834
|
+
const decimals = typeof rawDecimals === "number" ? rawDecimals : isNative ? 18 : void 0;
|
|
86835
|
+
const balance = decodeBalance(
|
|
86836
|
+
token.balance ?? token.amount ?? token.tokenBalance ?? token.uiAmount ?? null,
|
|
86837
|
+
decimals
|
|
86838
|
+
);
|
|
86048
86839
|
return {
|
|
86049
|
-
symbol: token.symbol ?? metadata93?.symbol ?? token.tokenSymbol ?? null,
|
|
86840
|
+
symbol: token.symbol ?? metadata93?.symbol ?? token.tokenSymbol ?? (isNative ? NATIVE_SYMBOLS[chainLabel] ?? null : null),
|
|
86050
86841
|
name: token.name ?? metadata93?.name ?? null,
|
|
86051
|
-
address:
|
|
86052
|
-
balance
|
|
86053
|
-
usd_value: token.totalValueUsd ?? token.valueUsd ?? token.usd_value ?? token.totalValueUsdCents
|
|
86842
|
+
address: tokenAddress,
|
|
86843
|
+
balance,
|
|
86844
|
+
usd_value: token.totalValueUsd ?? token.valueUsd ?? token.usd_value ?? (typeof token.totalValueUsdCents === "number" ? token.totalValueUsdCents / 100 : usdFromPrices(token, balance))
|
|
86054
86845
|
};
|
|
86055
86846
|
});
|
|
86847
|
+
const held = summarized.filter(
|
|
86848
|
+
(token) => !isRecord3(token) || token.balance !== "0"
|
|
86849
|
+
);
|
|
86056
86850
|
return {
|
|
86057
86851
|
address,
|
|
86058
86852
|
chain: chainLabel,
|
|
86059
|
-
token_count:
|
|
86060
|
-
tokens:
|
|
86061
|
-
...
|
|
86062
|
-
note: `Showing the first ${MAX_CONCISE_TOKENS} of ${
|
|
86853
|
+
token_count: held.length,
|
|
86854
|
+
tokens: held.slice(0, MAX_CONCISE_TOKENS),
|
|
86855
|
+
...held.length > MAX_CONCISE_TOKENS ? {
|
|
86856
|
+
note: `Showing the first ${MAX_CONCISE_TOKENS} of ${held.length} held tokens (zero balances omitted). Use response_format=detailed for the full provider payload.`
|
|
86857
|
+
} : summarized.length > held.length ? {
|
|
86858
|
+
note: `${summarized.length - held.length} zero-balance tokens omitted. Use response_format=detailed for the full provider payload.`
|
|
86063
86859
|
} : {}
|
|
86064
86860
|
};
|
|
86065
86861
|
};
|
|
@@ -86074,12 +86870,13 @@ function heroTools(endpoints, options = {}) {
|
|
|
86074
86870
|
title: "Get Wallet Portfolio",
|
|
86075
86871
|
description: "Token holdings for a wallet with balances and USD values where available. EVM chains route through Alchemy; Solana wallets are detected automatically and route through Helius.",
|
|
86076
86872
|
inputSchema: zodToInputSchema2(walletPortfolioSchema),
|
|
86873
|
+
outputSchema: heroOutputSchema("get_wallet_portfolio"),
|
|
86077
86874
|
annotations: VOLATILE_READ_ANNOTATIONS
|
|
86078
86875
|
},
|
|
86079
86876
|
handler: async (_client, args) => {
|
|
86080
86877
|
const parsed = walletPortfolioSchema.safeParse(args ?? {});
|
|
86081
86878
|
if (!parsed.success) {
|
|
86082
|
-
return
|
|
86879
|
+
return errorResult3(
|
|
86083
86880
|
`Invalid arguments: ${JSON.stringify(parsed.error.format())}`
|
|
86084
86881
|
);
|
|
86085
86882
|
}
|
|
@@ -86094,7 +86891,7 @@ function heroTools(endpoints, options = {}) {
|
|
|
86094
86891
|
const normalized = normalizeChain(chain);
|
|
86095
86892
|
const network = ALCHEMY_NETWORKS[normalized];
|
|
86096
86893
|
if (!network) {
|
|
86097
|
-
return
|
|
86894
|
+
return errorResult3(
|
|
86098
86895
|
`Unknown chain '${chain}'. Pass an EVM chain name (${Object.keys(ALCHEMY_NETWORKS).slice(0, 6).join(", ")}, \u2026) or "solana".`
|
|
86099
86896
|
);
|
|
86100
86897
|
}
|
|
@@ -86109,6 +86906,9 @@ function heroTools(endpoints, options = {}) {
|
|
|
86109
86906
|
};
|
|
86110
86907
|
return [getTokenPrice, checkTokenSafety, getWalletPortfolio];
|
|
86111
86908
|
}
|
|
86909
|
+
function heroOutputSchema(name) {
|
|
86910
|
+
return HERO_TOOL_OUTPUT_SCHEMAS[name];
|
|
86911
|
+
}
|
|
86112
86912
|
function zodToInputSchema2(schema) {
|
|
86113
86913
|
const jsonSchema = schema.toJSONSchema();
|
|
86114
86914
|
const { $schema, ...rest } = jsonSchema;
|
|
@@ -86905,7 +87705,7 @@ var TASK_TOOLSET_ROUTES = {
|
|
|
86905
87705
|
steps: [
|
|
86906
87706
|
step(
|
|
86907
87707
|
"funding",
|
|
86908
|
-
"Read normalized funding across all five RWA venues with per-venue runtime status; respect funding_mechanism
|
|
87708
|
+
"Read normalized funding across all five RWA venues with per-venue runtime status; respect funding_mechanism, since both-sides rollover (borrow_apr_annual) is never sign-comparable with a funding transfer.",
|
|
86909
87709
|
"rwa_get_funding_rates",
|
|
86910
87710
|
"rwa_get_hyperliquid_dex_perps",
|
|
86911
87711
|
`${PROVIDER_FALLBACK} The flagship builder dexes carry the deepest RWA books when the full fan-out is unavailable.`
|
|
@@ -87156,6 +87956,10 @@ var TASK_TOOLSET_CATALOG = [
|
|
|
87156
87956
|
"get_derivatives_tickers",
|
|
87157
87957
|
"get_open_interest_history",
|
|
87158
87958
|
"get_funding_rate_history",
|
|
87959
|
+
// Hive Archive: cross-venue derivatives history since 2023-11 for
|
|
87960
|
+
// windows older than live venue APIs retain.
|
|
87961
|
+
"archive_get_funding_history",
|
|
87962
|
+
"archive_get_open_interest_history",
|
|
87159
87963
|
"hyperliquid_get_perp_dexs",
|
|
87160
87964
|
"hyperliquid_get_perps_at_oi_cap",
|
|
87161
87965
|
"hyperliquid_get_meta",
|
|
@@ -87823,7 +88627,7 @@ var TASK_TOOLSET_CATALOG = [
|
|
|
87823
88627
|
id: "rwa_perp_analysis",
|
|
87824
88628
|
kind: "product",
|
|
87825
88629
|
label: "RWA Perp Analysis",
|
|
87826
|
-
purpose: "Analyze tokenized real-world-asset perps
|
|
88630
|
+
purpose: "Analyze tokenized real-world-asset perps (stocks, pre-IPO names, ETFs, indices, commodities, FX) across Hyperliquid HIP-3 builder dexes, Ostium, Avantis, Lighter, and Extended: cross-venue funding, mechanism-true carry, open interest, and price-suspect checks.",
|
|
87827
88631
|
outcome: "A cross-venue RWA perp report with normalized funding (raw + annualized per venue mechanism), asset-class and delisted/price-suspect flags on every row, per-venue runtime status, and receipts.",
|
|
87828
88632
|
requiredIdentifiers: ["underlying ticker, venue, or asset class"],
|
|
87829
88633
|
optionalIdentifiers: ["builder dex name", "time range"],
|
|
@@ -88852,7 +89656,9 @@ function redisAdapter() {
|
|
|
88852
89656
|
if (!redis || !isRedisAvailable()) {
|
|
88853
89657
|
throw new Error("Redis is not available");
|
|
88854
89658
|
}
|
|
88855
|
-
await redis.
|
|
89659
|
+
const results = await redis.pipeline().lpush(key, value).ltrim(key, 0, limit - 1).exec();
|
|
89660
|
+
const failure = results?.find(([error]) => error)?.[0];
|
|
89661
|
+
if (failure) throw failure;
|
|
88856
89662
|
}
|
|
88857
89663
|
};
|
|
88858
89664
|
}
|
|
@@ -88929,13 +89735,8 @@ var CanaryHistoryStore = class {
|
|
|
88929
89735
|
0,
|
|
88930
89736
|
this.limit
|
|
88931
89737
|
);
|
|
88932
|
-
const
|
|
88933
|
-
|
|
88934
|
-
);
|
|
88935
|
-
const memoryOnly = this.memory.some(
|
|
88936
|
-
(entry) => !persistedValues.has(JSON.stringify(entry))
|
|
88937
|
-
);
|
|
88938
|
-
const hasUnpersisted = this.unpersisted.size > 0 || memoryOnly;
|
|
89738
|
+
const storeLostEverything = this.memory.length > 0 && entries.length === 0;
|
|
89739
|
+
const hasUnpersisted = this.unpersisted.size > 0 || storeLostEverything;
|
|
88939
89740
|
const requiredUnavailable = this.backend === "redis" && hasUnpersisted;
|
|
88940
89741
|
if (!hasUnpersisted) this.lastPersistenceError = null;
|
|
88941
89742
|
return {
|
|
@@ -89578,7 +90379,7 @@ var HIVE_RESOURCES = [
|
|
|
89578
90379
|
];
|
|
89579
90380
|
var RUNTIME_STATUS_SEMANTICS = {
|
|
89580
90381
|
ok: "Provider call is expected to work with current configuration.",
|
|
89581
|
-
invalid_input: "The arguments failed validation or named an unsupported venue/method. The provider was not the problem
|
|
90382
|
+
invalid_input: "The arguments failed validation or named an unsupported venue/method. The provider was not the problem: fix the input (check the schema, use a supported value) and retry. This is NOT an outage, auth, rate-limit, or plan issue.",
|
|
89582
90383
|
missing_key: "Provider credentials are not configured for this runtime.",
|
|
89583
90384
|
plan_required: "The tool exists but the upstream account plan, quota, or credits prevent execution.",
|
|
89584
90385
|
rate_limited: "The upstream or Hive rate limit was hit. Retry after backoff or Retry-After.",
|
|
@@ -90337,7 +91138,7 @@ async function runMcpProtocolHealth(options = {}) {
|
|
|
90337
91138
|
}
|
|
90338
91139
|
|
|
90339
91140
|
// src/health/oauthLifecycle.ts
|
|
90340
|
-
import { createHash as
|
|
91141
|
+
import { createHash as createHash6, randomBytes as randomBytes3 } from "crypto";
|
|
90341
91142
|
|
|
90342
91143
|
// src/auth/oauth/provider.ts
|
|
90343
91144
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
@@ -90713,10 +91514,10 @@ function isHiveOAuthAccessToken(token) {
|
|
|
90713
91514
|
}
|
|
90714
91515
|
|
|
90715
91516
|
// src/auth/oauth/store.ts
|
|
90716
|
-
import { createHash as
|
|
91517
|
+
import { createHash as createHash5 } from "crypto";
|
|
90717
91518
|
var REVOKED_FAMILY_TTL_SECONDS = 7776e3;
|
|
90718
91519
|
function digest(value) {
|
|
90719
|
-
return
|
|
91520
|
+
return createHash5("sha256").update(value).digest("hex");
|
|
90720
91521
|
}
|
|
90721
91522
|
function parseJson(raw) {
|
|
90722
91523
|
if (!raw) return void 0;
|
|
@@ -91168,7 +91969,7 @@ async function operationRejects(operation) {
|
|
|
91168
91969
|
}
|
|
91169
91970
|
async function issueCanaryFamily(args) {
|
|
91170
91971
|
const verifier = randomBytes3(48).toString("base64url");
|
|
91171
|
-
const challenge =
|
|
91972
|
+
const challenge = createHash6("sha256").update(verifier).digest("base64url");
|
|
91172
91973
|
let consentLocation;
|
|
91173
91974
|
const response = {
|
|
91174
91975
|
redirect(status, location) {
|
|
@@ -91480,7 +92281,7 @@ async function runOAuthLifecycleHealth(overrides = {}) {
|
|
|
91480
92281
|
import Coingecko2 from "@coingecko/coingecko-typescript";
|
|
91481
92282
|
|
|
91482
92283
|
// src/tools/goplus/goplusClient.ts
|
|
91483
|
-
import { createHash as
|
|
92284
|
+
import { createHash as createHash7 } from "crypto";
|
|
91484
92285
|
function parsePositiveInteger(value, fallback) {
|
|
91485
92286
|
const parsed = Number.parseInt(value || "", 10);
|
|
91486
92287
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
@@ -91574,7 +92375,7 @@ var GoPlusClient = class {
|
|
|
91574
92375
|
const time = Math.floor(Date.now() / 1e3);
|
|
91575
92376
|
const timeStr = time.toString();
|
|
91576
92377
|
const signInput = this.appKey + timeStr + this.appSecret;
|
|
91577
|
-
const sign =
|
|
92378
|
+
const sign = createHash7("sha1").update(signInput).digest("hex");
|
|
91578
92379
|
secureLogger.info("GoPlus: requesting access token", {
|
|
91579
92380
|
appKeyLength: this.appKey.length,
|
|
91580
92381
|
appSecretLength: this.appSecret.length,
|
|
@@ -92884,6 +93685,7 @@ var PROVIDER_RESOURCE_PATTERNS = [
|
|
|
92884
93685
|
["defillama", /^(defillama|get-)/],
|
|
92885
93686
|
["hyperliquid", /^hyperliquid(?:[.-]|$)/],
|
|
92886
93687
|
["rwa", /^rwa(?:[.-]|$)/],
|
|
93688
|
+
["archive", /^archive(?:[.-]|$)/],
|
|
92887
93689
|
["fetch", /^fetch(?:[.-]|$)/],
|
|
92888
93690
|
["helius", /^helius\./],
|
|
92889
93691
|
["moralis", /^moralis\./],
|
|
@@ -92907,6 +93709,7 @@ function classifyProvider(endpoint) {
|
|
|
92907
93709
|
if (toolName.startsWith("defillama_")) return "defillama";
|
|
92908
93710
|
if (toolName.startsWith("hyperliquid_")) return "hyperliquid";
|
|
92909
93711
|
if (toolName.startsWith("rwa_")) return "rwa";
|
|
93712
|
+
if (toolName.startsWith("archive_")) return "archive";
|
|
92910
93713
|
if (toolName.startsWith("fetch_")) return "fetch";
|
|
92911
93714
|
if (toolName.startsWith("hive_")) return "hive";
|
|
92912
93715
|
return "coingecko";
|
|
@@ -92939,6 +93742,9 @@ var QUICK_PROBE_ARGS = {
|
|
|
92939
93742
|
hyperliquid: { toolName: "hyperliquid_get_perp_dexs", args: {} },
|
|
92940
93743
|
// Single-request keyless venue read (official Ostium subgraph).
|
|
92941
93744
|
rwa: { toolName: "rwa_get_ostium_markets", args: { limit: 5 } },
|
|
93745
|
+
// Cheap coverage probe (index-only extremes, 10-min in-process cache).
|
|
93746
|
+
// Unset archive credentials classify as missing-key skip, not failure.
|
|
93747
|
+
archive: { toolName: "archive_get_coverage", args: {} },
|
|
92942
93748
|
fetch: {
|
|
92943
93749
|
toolName: "fetch_public_api",
|
|
92944
93750
|
args: { host: "api.coingecko.com", path: "/api/v3/ping" }
|
|
@@ -93042,6 +93848,8 @@ var FULL_MODE_DEFAULT_ARGS = {
|
|
|
93042
93848
|
},
|
|
93043
93849
|
// Keyless RWA venue reads: bound every full-mode probe.
|
|
93044
93850
|
rwa: { limit: 10 },
|
|
93851
|
+
// Archive history reads: BTC exists in every dataset; keep probes bounded.
|
|
93852
|
+
archive: { base_coin: "BTC", limit: 5 },
|
|
93045
93853
|
fetch: {
|
|
93046
93854
|
host: "api.coingecko.com",
|
|
93047
93855
|
path: "/api/v3/ping"
|
|
@@ -94389,6 +95197,7 @@ function getClientForProvider(provider, clients) {
|
|
|
94389
95197
|
case "hyperliquid":
|
|
94390
95198
|
case "fetch":
|
|
94391
95199
|
case "defillama":
|
|
95200
|
+
case "archive":
|
|
94392
95201
|
return null;
|
|
94393
95202
|
case "codex":
|
|
94394
95203
|
return null;
|
|
@@ -95149,14 +95958,17 @@ var TASK_CANARY_PROBES = [
|
|
|
95149
95958
|
args: { query: "bitcoin" }
|
|
95150
95959
|
}
|
|
95151
95960
|
];
|
|
95961
|
+
var PROBE_SKIPPED = /* @__PURE__ */ Symbol("probe_skipped");
|
|
95152
95962
|
function classifyRuntimeStatus(probeStatus, error) {
|
|
95153
95963
|
if (probeStatus === "passed") return "ok";
|
|
95154
|
-
if (probeStatus === "skipped") return
|
|
95964
|
+
if (probeStatus === "skipped") return PROBE_SKIPPED;
|
|
95155
95965
|
return classifyRuntimeStatusFromError(error);
|
|
95156
95966
|
}
|
|
95157
95967
|
function aggregateTaskStatus(results) {
|
|
95158
95968
|
if (results.length === 0) return "unknown";
|
|
95159
|
-
|
|
95969
|
+
const observed = results.filter((result2) => !result2.skipped).map((result2) => result2.status);
|
|
95970
|
+
if (observed.length === 0) return "unknown";
|
|
95971
|
+
return aggregateRuntimeStatus(observed);
|
|
95160
95972
|
}
|
|
95161
95973
|
async function getTaskCanaryHistory(limit = 20) {
|
|
95162
95974
|
const safeLimit = Math.max(1, Math.min(limit, TASK_CANARY_HISTORY_LIMIT));
|
|
@@ -95219,11 +96031,14 @@ async function runTaskCanary(toolsetId) {
|
|
|
95219
96031
|
const toolset = TASK_TOOLSETS.find((item) => item.id === toolsetId);
|
|
95220
96032
|
if (!probe || !toolset) return null;
|
|
95221
96033
|
const result2 = await runSelfTestTool(probe.toolName, probe.args);
|
|
95222
|
-
const
|
|
96034
|
+
const classified = classifyRuntimeStatus(result2.status, result2.error);
|
|
96035
|
+
const skipped = classified === PROBE_SKIPPED;
|
|
96036
|
+
const status = skipped ? "missing_key" : classified;
|
|
95223
96037
|
return {
|
|
95224
96038
|
toolsetId,
|
|
95225
96039
|
label: toolset.label,
|
|
95226
96040
|
status,
|
|
96041
|
+
...skipped && { skipped: true },
|
|
95227
96042
|
probeToolName: probe.toolName,
|
|
95228
96043
|
provider: result2.provider,
|
|
95229
96044
|
description: probe.description,
|
|
@@ -95234,20 +96049,57 @@ async function runTaskCanary(toolsetId) {
|
|
|
95234
96049
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
95235
96050
|
};
|
|
95236
96051
|
}
|
|
96052
|
+
var TASK_CANARY_PROBE_TIMEOUT_MS = Number.parseInt(
|
|
96053
|
+
process.env.HIVE_TASK_CANARY_TIMEOUT_MS || "10000",
|
|
96054
|
+
10
|
|
96055
|
+
);
|
|
96056
|
+
var TASK_CANARY_CONCURRENCY = Number.parseInt(
|
|
96057
|
+
process.env.HIVE_TASK_CANARY_CONCURRENCY || "4",
|
|
96058
|
+
10
|
|
96059
|
+
);
|
|
96060
|
+
async function runTaskCanaryBounded(probe) {
|
|
96061
|
+
if (TASK_CANARY_PROBE_TIMEOUT_MS <= 0) return runTaskCanary(probe.toolsetId);
|
|
96062
|
+
const startedAt = Date.now();
|
|
96063
|
+
let timer;
|
|
96064
|
+
const timeout = new Promise((resolve) => {
|
|
96065
|
+
timer = setTimeout(() => resolve("timeout"), TASK_CANARY_PROBE_TIMEOUT_MS);
|
|
96066
|
+
});
|
|
96067
|
+
try {
|
|
96068
|
+
const outcome = await Promise.race([runTaskCanary(probe.toolsetId), timeout]);
|
|
96069
|
+
if (outcome !== "timeout") return outcome;
|
|
96070
|
+
const toolset = TASK_TOOLSETS.find((item) => item.id === probe.toolsetId);
|
|
96071
|
+
return {
|
|
96072
|
+
toolsetId: probe.toolsetId,
|
|
96073
|
+
label: toolset?.label ?? probe.toolsetId,
|
|
96074
|
+
status: "degraded",
|
|
96075
|
+
probeToolName: probe.toolName,
|
|
96076
|
+
provider: "unknown",
|
|
96077
|
+
description: probe.description,
|
|
96078
|
+
duration: Date.now() - startedAt,
|
|
96079
|
+
error: `Canary exceeded ${TASK_CANARY_PROBE_TIMEOUT_MS}ms and was abandoned; the provider is slow or backing off rather than returning.`,
|
|
96080
|
+
requiredIdentifiers: toolset?.requiredIdentifiers ?? [],
|
|
96081
|
+
stableFixtures: toolset?.stableFixtures ?? [],
|
|
96082
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
96083
|
+
};
|
|
96084
|
+
} finally {
|
|
96085
|
+
if (timer) clearTimeout(timer);
|
|
96086
|
+
}
|
|
96087
|
+
}
|
|
95237
96088
|
async function runTaskCanariesQuick() {
|
|
95238
96089
|
const start = Date.now();
|
|
95239
|
-
const results = [];
|
|
95240
96090
|
const interProbeDelayMs = Number.parseInt(
|
|
95241
96091
|
process.env.HIVE_TASK_CANARY_DELAY_MS || "350",
|
|
95242
96092
|
10
|
|
95243
96093
|
);
|
|
95244
|
-
|
|
95245
|
-
|
|
95246
|
-
|
|
95247
|
-
|
|
95248
|
-
|
|
95249
|
-
|
|
95250
|
-
|
|
96094
|
+
const settled = await mapWithConcurrency2(
|
|
96095
|
+
TASK_CANARY_PROBES,
|
|
96096
|
+
TASK_CANARY_CONCURRENCY,
|
|
96097
|
+
(probe) => runTaskCanaryBounded(probe),
|
|
96098
|
+
interProbeDelayMs
|
|
96099
|
+
);
|
|
96100
|
+
const results = settled.flatMap(
|
|
96101
|
+
(entry) => entry.status === "fulfilled" && entry.value ? [entry.value] : []
|
|
96102
|
+
);
|
|
95251
96103
|
const observedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
95252
96104
|
const summary = {
|
|
95253
96105
|
...getCurrentCanaryIdentity(observedAt),
|
|
@@ -97213,7 +98065,7 @@ var TASK_RESULT_VALIDATION_OUTPUT_SCHEMA = {
|
|
|
97213
98065
|
warnings: { type: "array", items: { type: "string" } }
|
|
97214
98066
|
}
|
|
97215
98067
|
};
|
|
97216
|
-
function
|
|
98068
|
+
function errorResult4(message) {
|
|
97217
98069
|
return {
|
|
97218
98070
|
content: [
|
|
97219
98071
|
{
|
|
@@ -97237,13 +98089,13 @@ function parseCursor(cursor) {
|
|
|
97237
98089
|
const parsed = Number.parseInt(cursor, 10);
|
|
97238
98090
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
|
|
97239
98091
|
}
|
|
97240
|
-
function
|
|
98092
|
+
function clampLimit4(limit, defaultLimit, maxLimit) {
|
|
97241
98093
|
if (!limit || !Number.isFinite(limit)) return defaultLimit;
|
|
97242
98094
|
return Math.min(Math.max(Math.floor(limit), 1), maxLimit);
|
|
97243
98095
|
}
|
|
97244
98096
|
function paginate(items, cursor, limit, defaultLimit, maxLimit) {
|
|
97245
98097
|
const offset = parseCursor(cursor);
|
|
97246
|
-
const pageSize =
|
|
98098
|
+
const pageSize = clampLimit4(limit, defaultLimit, maxLimit);
|
|
97247
98099
|
const page = items.slice(offset, offset + pageSize);
|
|
97248
98100
|
const nextOffset = offset + page.length;
|
|
97249
98101
|
const hasMore = nextOffset < items.length;
|
|
@@ -97775,14 +98627,14 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
97775
98627
|
},
|
|
97776
98628
|
handler: async (_client, args) => {
|
|
97777
98629
|
if (!args) {
|
|
97778
|
-
return
|
|
98630
|
+
return errorResult4(
|
|
97779
98631
|
"No endpoint provided. Pass an 'endpoint' (or 'endpoint_name') parameter with the tool name."
|
|
97780
98632
|
);
|
|
97781
98633
|
}
|
|
97782
98634
|
const parsedEndpointArgs = getEndpointSchema.parse(args);
|
|
97783
98635
|
const requestedEndpoint = parsedEndpointArgs.endpoint ?? parsedEndpointArgs.endpoint_name;
|
|
97784
98636
|
if (!requestedEndpoint) {
|
|
97785
|
-
return
|
|
98637
|
+
return errorResult4(
|
|
97786
98638
|
"No endpoint provided. Pass an 'endpoint' (or 'endpoint_name') parameter with the tool name."
|
|
97787
98639
|
);
|
|
97788
98640
|
}
|
|
@@ -97796,7 +98648,7 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
97796
98648
|
...categoryTools
|
|
97797
98649
|
].find((endpoint2) => endpoint2.tool.name === requestedEndpoint);
|
|
97798
98650
|
if (!lookup.ok && !metaEndpoint) {
|
|
97799
|
-
return
|
|
98651
|
+
return errorResult4(
|
|
97800
98652
|
`Endpoint '${lookup.resolvedToolName || lookup.requestedToolName}' not found. Use a category listing tool to discover available endpoints.`
|
|
97801
98653
|
);
|
|
97802
98654
|
}
|
|
@@ -97831,12 +98683,12 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
97831
98683
|
const invokeWithPolicy = async (invoker, client2, args) => {
|
|
97832
98684
|
const { success, data, error } = invokeEndpointSchema.safeParse(args);
|
|
97833
98685
|
if (!success) {
|
|
97834
|
-
return
|
|
98686
|
+
return errorResult4(
|
|
97835
98687
|
`Invalid arguments: ${JSON.stringify(error?.format())}`
|
|
97836
98688
|
);
|
|
97837
98689
|
}
|
|
97838
98690
|
const invokeArgs = normalizeInvokeArgs(data);
|
|
97839
|
-
if (!invokeArgs.ok) return
|
|
98691
|
+
if (!invokeArgs.ok) return errorResult4(invokeArgs.message);
|
|
97840
98692
|
const lookup = resolveEndpoint(endpoints, invokeArgs.resolvedToolName);
|
|
97841
98693
|
if (!lookup.ok) {
|
|
97842
98694
|
return createToolNotFoundResult(
|
|
@@ -97844,7 +98696,7 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
97844
98696
|
).mcpResult;
|
|
97845
98697
|
}
|
|
97846
98698
|
if (!endpointAllowedForInvoker(invoker, lookup.endpoint)) {
|
|
97847
|
-
return
|
|
98699
|
+
return errorResult4(invokerPolicyMessage(invoker, lookup.endpoint));
|
|
97848
98700
|
}
|
|
97849
98701
|
const executionContext = {
|
|
97850
98702
|
transport: "dynamic",
|
|
@@ -97918,7 +98770,7 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
97918
98770
|
handler: async (_client, args) => {
|
|
97919
98771
|
const parsed = validateTaskResultSchema.safeParse(args);
|
|
97920
98772
|
if (!parsed.success) {
|
|
97921
|
-
return
|
|
98773
|
+
return errorResult4(
|
|
97922
98774
|
`Invalid task result validation request: ${JSON.stringify(parsed.error.format())}`
|
|
97923
98775
|
);
|
|
97924
98776
|
}
|
|
@@ -97926,7 +98778,7 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
97926
98778
|
(entry) => entry.id === parsed.data.task_toolset_id
|
|
97927
98779
|
);
|
|
97928
98780
|
if (!toolset) {
|
|
97929
|
-
return
|
|
98781
|
+
return errorResult4(
|
|
97930
98782
|
`Unknown task toolset '${parsed.data.task_toolset_id}'. Use search_tools to select a canonical id.`
|
|
97931
98783
|
);
|
|
97932
98784
|
}
|
|
@@ -98497,7 +99349,7 @@ function buildServerInstructions(scope) {
|
|
|
98497
99349
|
].join(" ");
|
|
98498
99350
|
}
|
|
98499
99351
|
return [
|
|
98500
|
-
`Hive serves live crypto market data to AI agents over a compact ${ROOT_META_TOOL_COUNT}-tool root. Loop: search_tools -> get_api_endpoint_schema -> ${READ_ENDPOINT_INVOKER} for reads (${STATEFUL_WRITE_ENDPOINT_INVOKER} only after explicit user approval; validate_task_result before structured answers). Every material response carries a _hive receipt
|
|
99352
|
+
`Hive serves live crypto market data to AI agents over a compact ${ROOT_META_TOOL_COUNT}-tool root. Loop: search_tools -> get_api_endpoint_schema -> ${READ_ENDPOINT_INVOKER} for reads (${STATEFUL_WRITE_ENDPOINT_INVOKER} only after explicit user approval; validate_task_result before structured answers). Every material response carries a _hive receipt (provider, fetched_at, runtime_status, _hive.receipt_id); cite it when answering.`,
|
|
98501
99353
|
`For the three dominant intents call the hero tools directly: get_token_price, check_token_safety, get_wallet_portfolio (response_format "concise" default, "detailed" available).`,
|
|
98502
99354
|
`Degraded providers are reported, not hidden: search_tools marks each tool's availability, and unavailable tools return runtime_status with cause and next_action instead of data. Follow next_action; never retry an unchanged failing call in a loop.`,
|
|
98503
99355
|
`Discovery is free and cheap: resources hive://toolsets, hive://providers, hive://tools; the ${UNDERLYING_TOOL_COUNT}-tool catalog stays reachable through search_tools or scoped category endpoints. Use limit/page/per_page to keep responses small; a material execution costs 1 credit on the hosted service.`
|
|
@@ -99355,22 +100207,22 @@ function createHiveMcpServer(scope, options) {
|
|
|
99355
100207
|
|
|
99356
100208
|
export {
|
|
99357
100209
|
getEnvVar,
|
|
99358
|
-
hasEnvVars,
|
|
99359
100210
|
getCsvEnv,
|
|
99360
100211
|
getNodeEnv,
|
|
99361
100212
|
isProduction,
|
|
99362
100213
|
getPort,
|
|
99363
100214
|
getRateLimitMax,
|
|
100215
|
+
getCredentialedRateLimitMax,
|
|
99364
100216
|
getCorsAllowedOrigins,
|
|
99365
100217
|
isSupabaseConfigured,
|
|
99366
100218
|
getSupabaseAdmin,
|
|
99367
|
-
getSupabaseAuthClient,
|
|
99368
100219
|
secureLogger,
|
|
99369
100220
|
isB2BSubjectsEnabled,
|
|
99370
100221
|
hasSubjectSigningSecret,
|
|
99371
100222
|
buildSubjectBodyDigest,
|
|
99372
100223
|
resolveSubjectContext,
|
|
99373
100224
|
materializeSubjectContext,
|
|
100225
|
+
buildStatefulWriteAuditEvent,
|
|
99374
100226
|
recordSubjectAuditEvent,
|
|
99375
100227
|
runWithSubjectContext,
|
|
99376
100228
|
HIVE_AUTH_USER_ARG,
|
|
@@ -99385,7 +100237,9 @@ export {
|
|
|
99385
100237
|
trackOAuthFirstMaterialReceipt,
|
|
99386
100238
|
getRedisClient,
|
|
99387
100239
|
isRedisAvailable,
|
|
100240
|
+
waitForRedisAvailability,
|
|
99388
100241
|
closeRedisClient,
|
|
100242
|
+
isRedisConfigured,
|
|
99389
100243
|
PROVIDER_CATALOG,
|
|
99390
100244
|
getProviderRuntimeStatus,
|
|
99391
100245
|
READ_ENDPOINT_INVOKER,
|