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.
Files changed (49) hide show
  1. package/README.md +2 -2
  2. package/agent-skills/SKILL.md +1 -1
  3. package/agent-skills/packs/hive-build/SKILL.md +1 -1
  4. package/agent-skills/packs/hive-build-onboarding/SKILL.md +7 -7
  5. package/agent-skills/packs/hive-cli/SKILL.md +2 -2
  6. package/agent-skills/packs/hive-defi-research/SKILL.md +1 -1
  7. package/agent-skills/packs/hive-dex-pool-analysis/SKILL.md +1 -1
  8. package/agent-skills/packs/hive-market-research/SKILL.md +1 -1
  9. package/agent-skills/packs/hive-mcp/SKILL.md +1 -1
  10. package/agent-skills/packs/hive-network-infrastructure/SKILL.md +1 -1
  11. package/agent-skills/packs/hive-nft-research/SKILL.md +1 -1
  12. package/agent-skills/packs/hive-prediction-markets/SKILL.md +1 -1
  13. package/agent-skills/packs/hive-query/SKILL.md +1 -1
  14. package/agent-skills/packs/hive-security-risk/SKILL.md +1 -1
  15. package/agent-skills/packs/hive-solana-analysis/SKILL.md +1 -1
  16. package/agent-skills/packs/hive-stateful-monitoring/SKILL.md +1 -1
  17. package/agent-skills/packs/hive-token-diligence/SKILL.md +1 -1
  18. package/agent-skills/packs/hive-tool-discovery/SKILL.md +1 -1
  19. package/agent-skills/packs/hive-wallet-investigation/SKILL.md +1 -1
  20. package/build/{api-client-FTVDFW5V.js → api-client-WMWFWXC7.js} +3 -15
  21. package/build/{auth-EZFJ5RYB.js → auth-Y4GXW376.js} +1 -9
  22. package/build/browser-auth-F62BMD7W.js +20 -0
  23. package/build/{chunk-W2QL7LYI.js → chunk-AGM2CIRL.js} +971 -117
  24. package/build/{chunk-7GD2MFYI.js → chunk-BDO27NIP.js} +37 -17
  25. package/build/{upgrade-J4HIGCKE.js → chunk-GB4XMTDE.js} +3 -4
  26. package/build/{chunk-TNQ7GJ6Q.js → chunk-GWZIAAQ2.js} +58 -40
  27. package/build/{chunk-Q7VBYLX7.js → chunk-P7NLFSZQ.js} +0 -58
  28. package/build/{chunk-N32UIHBU.js → chunk-SFN772RL.js} +5 -5
  29. package/build/{chunk-AEU43ACS.js → chunk-TVBPCELA.js} +2 -2
  30. package/build/{chunk-RUHO24SX.js → chunk-WF34ZD5I.js} +7 -162
  31. package/build/{chunk-I5Z6FZRL.js → chunk-XQPEFCSY.js} +14 -12
  32. package/build/cli.js +36 -47
  33. package/build/{completion-7KBW243H.js → completion-C57PDJMY.js} +1 -1
  34. package/build/{doctor-ORDOCGLM.js → doctor-XG5IQCXK.js} +5 -5
  35. package/build/{init-UCTCUYFA.js → init-COHA3VZI.js} +3 -3
  36. package/build/{init-all-BVWW3JVK.js → init-all-YTQJLPLR.js} +43 -24
  37. package/build/{mcpServer-RUEOTTF7.js → mcpServer-XHIN5JER.js} +1 -1
  38. package/build/monitor-worker.js +2 -2
  39. package/build/{namespace-L5DGCY7K.js → namespace-YLCGHVRT.js} +1 -1
  40. package/build/release.json +4 -4
  41. package/build/{serve-DKLR2WQH.js → serve-PWMT5IRU.js} +1 -1
  42. package/build/server.js +1475 -452
  43. package/build/stdio.js +1 -1
  44. package/build/{tools-I7GLWTV6.js → tools-XNFJBN7Q.js} +2 -2
  45. package/build/{uninstall-SAOIPL6K.js → uninstall-TKDP6C4F.js} +7 -7
  46. package/build/upgrade-ODPKMIDC.js +14 -0
  47. package/build/{watch-WVR4CGCD.js → watch-MXJSNGL2.js} +2 -2
  48. package/package.json +5 -3
  49. 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 = createClient2;
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 createClient2(options) {
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: createHash7 } = __require("crypto");
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 = createHash7("sha1").update(key + GUID).digest("base64");
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: createHash7 } = __require("crypto");
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 = createHash7("sha1").update(key + GUID).digest("base64");
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 DEFAULT_LIMIT2 = RESPONSE_LIMITS.DEFAULT_LIMITS.tokens;
46388
- const requestedLimit = limit || DEFAULT_LIMIT2;
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 DEFAULT_LIMIT2 = 30;
51870
- const limit = Math.min(args?.limit || DEFAULT_LIMIT2, MAX_SAFE_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 DEFAULT_LIMIT2 = 50;
52979
- const limit = Math.min(args?.limit || DEFAULT_LIMIT2, MAX_SAFE_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 DEFAULT_LIMIT2 = 50;
55096
- const limit = Math.min(args.limit || DEFAULT_LIMIT2, MAX_SAFE_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
- if (!/^(Get|Fetch|Retrieve|Search|Filter|Check|List|Return|Query)/i.test(
81135
- summary
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) return 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 createHash3 } from "crypto";
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 createHash3("sha256").update(canonicalizeHiveJson(value)).digest("hex");
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 errorResult2(message) {
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 errorResult2(
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 errorResult2(
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 errorResult2(
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 errorResult2(
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 errorResult2(
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.slice(0, MAX_CONCISE_TOKENS).map((token) => {
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: token.tokenAddress ?? token.mint ?? token.address ?? null,
86052
- balance: token.balance ?? token.amount ?? token.tokenBalance ?? token.uiAmount ?? null,
86053
- usd_value: token.totalValueUsd ?? token.valueUsd ?? token.usd_value ?? token.totalValueUsdCents ?? null
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: tokens.length,
86060
- tokens: summarized,
86061
- ...tokens.length > MAX_CONCISE_TOKENS ? {
86062
- note: `Showing the first ${MAX_CONCISE_TOKENS} of ${tokens.length} tokens. Use response_format=detailed for the full list.`
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 errorResult2(
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 errorResult2(
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 \u2014 both-sides rollover (borrow_apr_annual) is never sign-comparable with a funding transfer.",
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 \u2014 stocks, pre-IPO names, ETFs, indices, commodities, FX \u2014 across Hyperliquid HIP-3 builder dexes, Ostium, Avantis, Lighter, and Extended: cross-venue funding, mechanism-true carry, open interest, and price-suspect checks.",
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.multi().lpush(key, value).ltrim(key, 0, limit - 1).exec();
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 persistedValues = new Set(
88933
- entries.map((entry) => JSON.stringify(entry))
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 \u2014 fix the input (check the schema, use a supported value) and retry. This is NOT an outage, auth, rate-limit, or plan issue.",
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 createHash5, randomBytes as randomBytes3 } from "crypto";
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 createHash4 } from "crypto";
91517
+ import { createHash as createHash5 } from "crypto";
90717
91518
  var REVOKED_FAMILY_TTL_SECONDS = 7776e3;
90718
91519
  function digest(value) {
90719
- return createHash4("sha256").update(value).digest("hex");
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 = createHash5("sha256").update(verifier).digest("base64url");
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 createHash6 } from "crypto";
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 = createHash6("sha1").update(signInput).digest("hex");
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 "degraded";
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
- return aggregateRuntimeStatus(results.map((result2) => result2.status));
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 status = classifyRuntimeStatus(result2.status, result2.error);
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
- for (const probe of TASK_CANARY_PROBES) {
95245
- const result2 = await runTaskCanary(probe.toolsetId);
95246
- if (result2) results.push(result2);
95247
- if (interProbeDelayMs > 0) {
95248
- await new Promise((resolve) => setTimeout(resolve, interProbeDelayMs));
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 errorResult3(message) {
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 clampLimit3(limit, defaultLimit, maxLimit) {
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 = clampLimit3(limit, defaultLimit, maxLimit);
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 errorResult3(
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 errorResult3(
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 errorResult3(
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 errorResult3(
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 errorResult3(invokeArgs.message);
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 errorResult3(invokerPolicyMessage(invoker, lookup.endpoint));
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 errorResult3(
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 errorResult3(
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 \u2014 provider, fetched_at, runtime_status, _hive.receipt_id \u2014 cite it when answering.`,
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,