hive-intelligence 1.3.0 → 1.4.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 (43) hide show
  1. package/README.md +1 -1
  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-HRFQWQMZ.js} +1 -9
  22. package/build/browser-auth-KXIO5BHM.js +20 -0
  23. package/build/{chunk-I5Z6FZRL.js → chunk-3OZFBWFK.js} +14 -12
  24. package/build/{chunk-N32UIHBU.js → chunk-7DYHWY4Y.js} +1 -1
  25. package/build/{chunk-7GD2MFYI.js → chunk-AQQAPAER.js} +35 -15
  26. package/build/{upgrade-J4HIGCKE.js → chunk-GHCYCBA3.js} +1 -2
  27. package/build/{chunk-Q7VBYLX7.js → chunk-P7NLFSZQ.js} +0 -58
  28. package/build/{chunk-RUHO24SX.js → chunk-TVQJY47C.js} +2 -157
  29. package/build/{chunk-W2QL7LYI.js → chunk-UFTNBCSA.js} +885 -101
  30. package/build/cli.js +28 -39
  31. package/build/{doctor-ORDOCGLM.js → doctor-GHUAUFS2.js} +3 -3
  32. package/build/{init-all-BVWW3JVK.js → init-all-RG542NWG.js} +32 -13
  33. package/build/{mcpServer-RUEOTTF7.js → mcpServer-D7OXUDU5.js} +1 -1
  34. package/build/monitor-worker.js +2 -2
  35. package/build/release.json +4 -4
  36. package/build/{serve-DKLR2WQH.js → serve-K3IT7DS4.js} +1 -1
  37. package/build/server.js +1429 -415
  38. package/build/stdio.js +1 -1
  39. package/build/{tools-I7GLWTV6.js → tools-D72ZH6P5.js} +1 -1
  40. package/build/upgrade-4S5XT5JG.js +14 -0
  41. package/build/{watch-WVR4CGCD.js → watch-IOSRSCFE.js} +2 -2
  42. package/package.json +5 -3
  43. 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 = [
@@ -81843,9 +82437,90 @@ function getOutputSchemaForTool(toolName) {
81843
82437
  }
81844
82438
  return GENERIC_TOOL_OUTPUT_SCHEMA;
81845
82439
  }
82440
+ var SOURCE_RECEIPT_SCHEMA = {
82441
+ type: "object",
82442
+ additionalProperties: true,
82443
+ required: ["provider", "runtime_status", "fetched_at"],
82444
+ properties: {
82445
+ provider: { type: "string" },
82446
+ tool: { type: "string" },
82447
+ runtime_status: { type: "string", enum: [...RUNTIME_STATUSES] },
82448
+ fetched_at: { type: "string", format: "date-time" },
82449
+ cache_status: { type: "string" },
82450
+ source: { type: "string" },
82451
+ receipt_id: { type: "string" }
82452
+ },
82453
+ description: "Provenance for this observation: which upstream served it, its runtime status, and when it was fetched."
82454
+ };
82455
+ var RECEIPT_REF = {
82456
+ type: "object",
82457
+ additionalProperties: true,
82458
+ required: ["provider", "runtime_status", "fetched_at"],
82459
+ properties: {
82460
+ provider: { type: "string" },
82461
+ runtime_status: { type: "string", enum: [...RUNTIME_STATUSES] },
82462
+ fetched_at: { type: "string", format: "date-time" }
82463
+ }
82464
+ };
82465
+ var GET_TOKEN_PRICE_OUTPUT_SCHEMA = {
82466
+ type: "object",
82467
+ additionalProperties: true,
82468
+ required: ["source_receipt"],
82469
+ properties: {
82470
+ source_receipt: RECEIPT_REF,
82471
+ token: { type: "string" },
82472
+ vs_currency: { type: "string" },
82473
+ price: { type: "number" },
82474
+ market_cap: { type: "number" },
82475
+ change_24h_percent: { type: "number" }
82476
+ }
82477
+ };
82478
+ var CHECK_TOKEN_SAFETY_OUTPUT_SCHEMA = {
82479
+ type: "object",
82480
+ additionalProperties: true,
82481
+ required: ["source_receipt"],
82482
+ properties: {
82483
+ source_receipt: RECEIPT_REF,
82484
+ address: { type: "string" },
82485
+ chain: { type: "string" },
82486
+ risk_flags: {
82487
+ type: "object",
82488
+ additionalProperties: true,
82489
+ description: "Raw provider risk datapoints; '1' means flagged. A missing key means the provider did not report that signal, not that it passed."
82490
+ },
82491
+ holder_count: {},
82492
+ token_name: { type: "string" },
82493
+ note: { type: "string" }
82494
+ }
82495
+ };
82496
+ var GET_WALLET_PORTFOLIO_OUTPUT_SCHEMA = {
82497
+ type: "object",
82498
+ additionalProperties: true,
82499
+ required: ["source_receipt"],
82500
+ properties: {
82501
+ source_receipt: RECEIPT_REF,
82502
+ address: { type: "string" },
82503
+ chain: { type: "string" },
82504
+ token_count: {
82505
+ type: "number",
82506
+ description: "Total holdings found, which may exceed tokens.length."
82507
+ },
82508
+ tokens: {
82509
+ type: "array",
82510
+ items: { type: "object", additionalProperties: true },
82511
+ description: "Holdings; truncated in concise mode when note is present."
82512
+ },
82513
+ note: { type: "string" }
82514
+ }
82515
+ };
82516
+ var HERO_TOOL_OUTPUT_SCHEMAS = {
82517
+ get_token_price: GET_TOKEN_PRICE_OUTPUT_SCHEMA,
82518
+ check_token_safety: CHECK_TOKEN_SAFETY_OUTPUT_SCHEMA,
82519
+ get_wallet_portfolio: GET_WALLET_PORTFOLIO_OUTPUT_SCHEMA
82520
+ };
81846
82521
 
81847
82522
  // src/utils/analytics.ts
81848
- import { randomUUID } from "crypto";
82523
+ import { createHash as createHash3, randomUUID } from "crypto";
81849
82524
  var GA_ENDPOINT = "https://www.google-analytics.com/mp/collect";
81850
82525
  function getMeasurementId() {
81851
82526
  return getEnvVar("GA_MEASUREMENT_ID");
@@ -81876,7 +82551,9 @@ async function sendEvents(clientId, events) {
81876
82551
  }
81877
82552
  }
81878
82553
  function deriveClientId(ip, userAgent, userId) {
81879
- if (userId) return userId;
82554
+ if (userId) {
82555
+ return `u_${createHash3("sha256").update(userId).digest("hex").slice(0, 32)}`;
82556
+ }
81880
82557
  const raw = `${ip || "unknown"}_${userAgent || "unknown"}`;
81881
82558
  let hash = 0;
81882
82559
  for (let i = 0; i < raw.length; i++) {
@@ -82020,24 +82697,32 @@ async function closeRedisClient() {
82020
82697
  current.disconnect();
82021
82698
  }
82022
82699
  }
82700
+ var DEGRADED_AFTER_ATTEMPTS = 3;
82701
+ var MAX_RECONNECT_DELAY_MS = 3e3;
82702
+ function redisRetryStrategy(times) {
82703
+ if (times === DEGRADED_AFTER_ATTEMPTS) {
82704
+ secureLogger.warn(
82705
+ `[redis] Connection failed after ${DEGRADED_AFTER_ATTEMPTS} retries, falling back to in-memory while reconnection continues`,
82706
+ {}
82707
+ );
82708
+ available = false;
82709
+ }
82710
+ return Math.min(times * 100, MAX_RECONNECT_DELAY_MS);
82711
+ }
82712
+ function redisClientOptions() {
82713
+ return {
82714
+ maxRetriesPerRequest: 3,
82715
+ retryStrategy: redisRetryStrategy,
82716
+ lazyConnect: true
82717
+ };
82718
+ }
82023
82719
  var redisUrl = getEnvVar("REDIS_URL");
82720
+ function isRedisConfigured() {
82721
+ return Boolean(redisUrl);
82722
+ }
82024
82723
  if (redisUrl) {
82025
82724
  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
- });
82725
+ client = new Redis(redisUrl, redisClientOptions());
82041
82726
  client.connect().then(() => {
82042
82727
  available = true;
82043
82728
  logRedisInfo("[redis] Connected successfully");
@@ -82313,6 +82998,24 @@ var PROVIDER_CATALOG = [
82313
82998
  alwaysAvailable: true,
82314
82999
  matches: (toolName, metadata93) => matchesProvider("hyperliquid", toolName, metadata93)
82315
83000
  },
83001
+ {
83002
+ // The Hive-native hive_archive_monitor tool contains the "_archive_"
83003
+ // infix this matcher would accept; the "hive" entry is listed first and
83004
+ // catalog order decides classification, so hive_ tools never land here.
83005
+ id: "archive",
83006
+ name: "Hive Archive",
83007
+ prefix: "archive_",
83008
+ 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",
83009
+ clientStrategy: "internal",
83010
+ credentialRequirement: {
83011
+ label: "SHARPE_ARCHIVE_SUPABASE_URL and SHARPE_ARCHIVE_SUPABASE_SERVICE_ROLE_KEY",
83012
+ isConfigured: () => hasAllEnvVars(
83013
+ "SHARPE_ARCHIVE_SUPABASE_URL",
83014
+ "SHARPE_ARCHIVE_SUPABASE_SERVICE_ROLE_KEY"
83015
+ )
83016
+ },
83017
+ matches: (toolName, metadata93) => matchesProvider("archive", toolName, metadata93)
83018
+ },
82316
83019
  {
82317
83020
  id: "fetch",
82318
83021
  name: "Open Data Fetch",
@@ -83089,6 +83792,7 @@ function getAllWrappedEndpoints() {
83089
83792
  ...getAllAlchemyEndpoints(),
83090
83793
  ...getAllHyperliquidEndpoints(),
83091
83794
  ...getAllRwaEndpoints(),
83795
+ ...getAllArchiveEndpoints(),
83092
83796
  ...getAllFetchEndpoints()
83093
83797
  ];
83094
83798
  const allEndpointsJSON = wrapper;
@@ -83163,6 +83867,7 @@ function getAllWrappedEndpoints() {
83163
83867
  "hive_",
83164
83868
  "hyperliquid_",
83165
83869
  "rwa_",
83870
+ "archive_",
83166
83871
  "fetch_"
83167
83872
  ];
83168
83873
  if (selfDescribedPrefixes.some((p) => tool89.name.startsWith(p))) {
@@ -83222,7 +83927,7 @@ var ToolRegistry = [
83222
83927
  {
83223
83928
  category: "Market Data and Price",
83224
83929
  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.",
83930
+ 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
83931
  tools: [
83227
83932
  "get_coins_index",
83228
83933
  "get_coins_market_data",
@@ -83289,6 +83994,15 @@ var ToolRegistry = [
83289
83994
  "rwa_get_avantis_markets",
83290
83995
  "rwa_get_lighter_markets",
83291
83996
  "rwa_get_extended_markets",
83997
+ // Hive Archive cross-venue derivatives history (since 2023-11).
83998
+ // Market data by fit, same reasoning as the RWA slice — the registry
83999
+ // stays at 10 categories (no 11th category surface).
84000
+ "archive_get_funding_history",
84001
+ "archive_get_open_interest_history",
84002
+ "archive_get_basis_history",
84003
+ "archive_get_long_short_history",
84004
+ "archive_get_liquidations_history",
84005
+ "archive_get_coverage",
83292
84006
  "get_stablecoins",
83293
84007
  "get_stablecoin_chains",
83294
84008
  "get_stablecoin_charts_global",
@@ -83948,7 +84662,7 @@ import { z as z15 } from "zod";
83948
84662
  import { randomUUID as randomUUID2 } from "crypto";
83949
84663
 
83950
84664
  // packages/mcp-client/src/receipt-integrity.ts
83951
- import { createHash as createHash3 } from "crypto";
84665
+ import { createHash as createHash4 } from "crypto";
83952
84666
  function compareJsonKeys(left, right) {
83953
84667
  if (left === right) return 0;
83954
84668
  return left < right ? -1 : 1;
@@ -83973,7 +84687,7 @@ function canonicalizeHiveJson(value) {
83973
84687
  }
83974
84688
  }
83975
84689
  function digestHiveCanonicalJson(value) {
83976
- return createHash3("sha256").update(canonicalizeHiveJson(value)).digest("hex");
84690
+ return createHash4("sha256").update(canonicalizeHiveJson(value)).digest("hex");
83977
84691
  }
83978
84692
 
83979
84693
  // src/utils/jsonSchemaValidator.ts
@@ -84785,6 +85499,7 @@ function getMcpCreditCost(toolName) {
84785
85499
  // src/services/analytics-writer.ts
84786
85500
  import { createHmac as createHmac2 } from "crypto";
84787
85501
  var PRINCIPAL_HASH_DOMAIN = "hive-analytics-principal-v1";
85502
+ var DROP_LOG_INTERVAL_MS = 6e4;
84788
85503
  var MAX_QUEUE_SIZE = 500;
84789
85504
  var FLUSH_INTERVAL_MS = 5e3;
84790
85505
  var FLUSH_BATCH_SIZE = 100;
@@ -84817,6 +85532,7 @@ function createAnalyticsWriter(overrides = {}) {
84817
85532
  };
84818
85533
  const queue = [];
84819
85534
  let dropped = 0;
85535
+ let lastDropLoggedAt = 0;
84820
85536
  let timer = null;
84821
85537
  let flushing = null;
84822
85538
  function hashPrincipal(raw) {
@@ -84876,6 +85592,14 @@ function createAnalyticsWriter(overrides = {}) {
84876
85592
  if (!deps.isConfigured()) return false;
84877
85593
  if (queue.length >= deps.maxQueueSize) {
84878
85594
  dropped += 1;
85595
+ const now = Date.now();
85596
+ if (now - lastDropLoggedAt >= DROP_LOG_INTERVAL_MS) {
85597
+ lastDropLoggedAt = now;
85598
+ secureLogger.warn(
85599
+ "Analytics queue is full \u2014 product-analytics rows are being dropped",
85600
+ { table, queueSize: queue.length, droppedTotal: dropped }
85601
+ );
85602
+ }
84879
85603
  return false;
84880
85604
  }
84881
85605
  queue.push({ table, row });
@@ -85771,7 +86495,7 @@ function isSolanaChain(chain, address) {
85771
86495
  if (chain) return SOLANA_CHAIN_NAMES.has(normalizeChain(chain));
85772
86496
  return !address.trim().toLowerCase().startsWith("0x");
85773
86497
  }
85774
- function errorResult2(message) {
86498
+ function errorResult3(message) {
85775
86499
  return {
85776
86500
  content: [{ type: "text", text: JSON.stringify({ error: message }) }],
85777
86501
  isError: true
@@ -85805,7 +86529,7 @@ function heroTools(endpoints, options = {}) {
85805
86529
  const executeUnderlying = async (toolName, args) => {
85806
86530
  const lookup = resolveEndpoint(endpoints, toolName);
85807
86531
  if (!lookup.ok) {
85808
- return errorResult2(
86532
+ return errorResult3(
85809
86533
  `Underlying endpoint '${toolName}' is not registered. Call search_tools to locate the current tool for this intent.`
85810
86534
  );
85811
86535
  }
@@ -85890,12 +86614,13 @@ function heroTools(endpoints, options = {}) {
85890
86614
  title: "Get Token Price",
85891
86615
  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
86616
  inputSchema: zodToInputSchema2(tokenPriceSchema),
86617
+ outputSchema: heroOutputSchema("get_token_price"),
85893
86618
  annotations: VOLATILE_READ_ANNOTATIONS
85894
86619
  },
85895
86620
  handler: async (_client, args) => {
85896
86621
  const parsed = tokenPriceSchema.safeParse(args ?? {});
85897
86622
  if (!parsed.success) {
85898
- return errorResult2(
86623
+ return errorResult3(
85899
86624
  `Invalid arguments: ${JSON.stringify(parsed.error.format())}`
85900
86625
  );
85901
86626
  }
@@ -85911,7 +86636,7 @@ function heroTools(endpoints, options = {}) {
85911
86636
  return respond(result3, format, trimOnchainPrice(address));
85912
86637
  }
85913
86638
  if (!token) {
85914
- return errorResult2(
86639
+ return errorResult3(
85915
86640
  'Pass token (CoinGecko id or symbol, for example "bitcoin" or "BTC"), or chain + address for a contract.'
85916
86641
  );
85917
86642
  }
@@ -85990,12 +86715,13 @@ function heroTools(endpoints, options = {}) {
85990
86715
  title: "Check Token Safety",
85991
86716
  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
86717
  inputSchema: zodToInputSchema2(tokenSafetySchema),
86718
+ outputSchema: heroOutputSchema("check_token_safety"),
85993
86719
  annotations: VOLATILE_READ_ANNOTATIONS
85994
86720
  },
85995
86721
  handler: async (_client, args) => {
85996
86722
  const parsed = tokenSafetySchema.safeParse(args ?? {});
85997
86723
  if (!parsed.success) {
85998
- return errorResult2(
86724
+ return errorResult3(
85999
86725
  `Invalid arguments: ${JSON.stringify(parsed.error.format())}`
86000
86726
  );
86001
86727
  }
@@ -86010,7 +86736,7 @@ function heroTools(endpoints, options = {}) {
86010
86736
  const normalized = normalizeChain(chain);
86011
86737
  const chainId = /^\d+$/.test(normalized) ? normalized : GOPLUS_CHAIN_IDS[normalized];
86012
86738
  if (!chainId) {
86013
- return errorResult2(
86739
+ return errorResult3(
86014
86740
  `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
86741
  );
86016
86742
  }
@@ -86074,12 +86800,13 @@ function heroTools(endpoints, options = {}) {
86074
86800
  title: "Get Wallet Portfolio",
86075
86801
  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
86802
  inputSchema: zodToInputSchema2(walletPortfolioSchema),
86803
+ outputSchema: heroOutputSchema("get_wallet_portfolio"),
86077
86804
  annotations: VOLATILE_READ_ANNOTATIONS
86078
86805
  },
86079
86806
  handler: async (_client, args) => {
86080
86807
  const parsed = walletPortfolioSchema.safeParse(args ?? {});
86081
86808
  if (!parsed.success) {
86082
- return errorResult2(
86809
+ return errorResult3(
86083
86810
  `Invalid arguments: ${JSON.stringify(parsed.error.format())}`
86084
86811
  );
86085
86812
  }
@@ -86094,7 +86821,7 @@ function heroTools(endpoints, options = {}) {
86094
86821
  const normalized = normalizeChain(chain);
86095
86822
  const network = ALCHEMY_NETWORKS[normalized];
86096
86823
  if (!network) {
86097
- return errorResult2(
86824
+ return errorResult3(
86098
86825
  `Unknown chain '${chain}'. Pass an EVM chain name (${Object.keys(ALCHEMY_NETWORKS).slice(0, 6).join(", ")}, \u2026) or "solana".`
86099
86826
  );
86100
86827
  }
@@ -86109,6 +86836,9 @@ function heroTools(endpoints, options = {}) {
86109
86836
  };
86110
86837
  return [getTokenPrice, checkTokenSafety, getWalletPortfolio];
86111
86838
  }
86839
+ function heroOutputSchema(name) {
86840
+ return HERO_TOOL_OUTPUT_SCHEMAS[name];
86841
+ }
86112
86842
  function zodToInputSchema2(schema) {
86113
86843
  const jsonSchema = schema.toJSONSchema();
86114
86844
  const { $schema, ...rest } = jsonSchema;
@@ -87156,6 +87886,10 @@ var TASK_TOOLSET_CATALOG = [
87156
87886
  "get_derivatives_tickers",
87157
87887
  "get_open_interest_history",
87158
87888
  "get_funding_rate_history",
87889
+ // Hive Archive: cross-venue derivatives history since 2023-11 for
87890
+ // windows older than live venue APIs retain.
87891
+ "archive_get_funding_history",
87892
+ "archive_get_open_interest_history",
87159
87893
  "hyperliquid_get_perp_dexs",
87160
87894
  "hyperliquid_get_perps_at_oi_cap",
87161
87895
  "hyperliquid_get_meta",
@@ -88852,7 +89586,9 @@ function redisAdapter() {
88852
89586
  if (!redis || !isRedisAvailable()) {
88853
89587
  throw new Error("Redis is not available");
88854
89588
  }
88855
- await redis.multi().lpush(key, value).ltrim(key, 0, limit - 1).exec();
89589
+ const results = await redis.pipeline().lpush(key, value).ltrim(key, 0, limit - 1).exec();
89590
+ const failure = results?.find(([error]) => error)?.[0];
89591
+ if (failure) throw failure;
88856
89592
  }
88857
89593
  };
88858
89594
  }
@@ -88929,13 +89665,8 @@ var CanaryHistoryStore = class {
88929
89665
  0,
88930
89666
  this.limit
88931
89667
  );
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;
89668
+ const storeLostEverything = this.memory.length > 0 && entries.length === 0;
89669
+ const hasUnpersisted = this.unpersisted.size > 0 || storeLostEverything;
88939
89670
  const requiredUnavailable = this.backend === "redis" && hasUnpersisted;
88940
89671
  if (!hasUnpersisted) this.lastPersistenceError = null;
88941
89672
  return {
@@ -90337,7 +91068,7 @@ async function runMcpProtocolHealth(options = {}) {
90337
91068
  }
90338
91069
 
90339
91070
  // src/health/oauthLifecycle.ts
90340
- import { createHash as createHash5, randomBytes as randomBytes3 } from "crypto";
91071
+ import { createHash as createHash6, randomBytes as randomBytes3 } from "crypto";
90341
91072
 
90342
91073
  // src/auth/oauth/provider.ts
90343
91074
  import { randomBytes as randomBytes2 } from "crypto";
@@ -90713,10 +91444,10 @@ function isHiveOAuthAccessToken(token) {
90713
91444
  }
90714
91445
 
90715
91446
  // src/auth/oauth/store.ts
90716
- import { createHash as createHash4 } from "crypto";
91447
+ import { createHash as createHash5 } from "crypto";
90717
91448
  var REVOKED_FAMILY_TTL_SECONDS = 7776e3;
90718
91449
  function digest(value) {
90719
- return createHash4("sha256").update(value).digest("hex");
91450
+ return createHash5("sha256").update(value).digest("hex");
90720
91451
  }
90721
91452
  function parseJson(raw) {
90722
91453
  if (!raw) return void 0;
@@ -91168,7 +91899,7 @@ async function operationRejects(operation) {
91168
91899
  }
91169
91900
  async function issueCanaryFamily(args) {
91170
91901
  const verifier = randomBytes3(48).toString("base64url");
91171
- const challenge = createHash5("sha256").update(verifier).digest("base64url");
91902
+ const challenge = createHash6("sha256").update(verifier).digest("base64url");
91172
91903
  let consentLocation;
91173
91904
  const response = {
91174
91905
  redirect(status, location) {
@@ -91480,7 +92211,7 @@ async function runOAuthLifecycleHealth(overrides = {}) {
91480
92211
  import Coingecko2 from "@coingecko/coingecko-typescript";
91481
92212
 
91482
92213
  // src/tools/goplus/goplusClient.ts
91483
- import { createHash as createHash6 } from "crypto";
92214
+ import { createHash as createHash7 } from "crypto";
91484
92215
  function parsePositiveInteger(value, fallback) {
91485
92216
  const parsed = Number.parseInt(value || "", 10);
91486
92217
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
@@ -91574,7 +92305,7 @@ var GoPlusClient = class {
91574
92305
  const time = Math.floor(Date.now() / 1e3);
91575
92306
  const timeStr = time.toString();
91576
92307
  const signInput = this.appKey + timeStr + this.appSecret;
91577
- const sign = createHash6("sha1").update(signInput).digest("hex");
92308
+ const sign = createHash7("sha1").update(signInput).digest("hex");
91578
92309
  secureLogger.info("GoPlus: requesting access token", {
91579
92310
  appKeyLength: this.appKey.length,
91580
92311
  appSecretLength: this.appSecret.length,
@@ -92884,6 +93615,7 @@ var PROVIDER_RESOURCE_PATTERNS = [
92884
93615
  ["defillama", /^(defillama|get-)/],
92885
93616
  ["hyperliquid", /^hyperliquid(?:[.-]|$)/],
92886
93617
  ["rwa", /^rwa(?:[.-]|$)/],
93618
+ ["archive", /^archive(?:[.-]|$)/],
92887
93619
  ["fetch", /^fetch(?:[.-]|$)/],
92888
93620
  ["helius", /^helius\./],
92889
93621
  ["moralis", /^moralis\./],
@@ -92907,6 +93639,7 @@ function classifyProvider(endpoint) {
92907
93639
  if (toolName.startsWith("defillama_")) return "defillama";
92908
93640
  if (toolName.startsWith("hyperliquid_")) return "hyperliquid";
92909
93641
  if (toolName.startsWith("rwa_")) return "rwa";
93642
+ if (toolName.startsWith("archive_")) return "archive";
92910
93643
  if (toolName.startsWith("fetch_")) return "fetch";
92911
93644
  if (toolName.startsWith("hive_")) return "hive";
92912
93645
  return "coingecko";
@@ -92939,6 +93672,9 @@ var QUICK_PROBE_ARGS = {
92939
93672
  hyperliquid: { toolName: "hyperliquid_get_perp_dexs", args: {} },
92940
93673
  // Single-request keyless venue read (official Ostium subgraph).
92941
93674
  rwa: { toolName: "rwa_get_ostium_markets", args: { limit: 5 } },
93675
+ // Cheap coverage probe (index-only extremes, 10-min in-process cache).
93676
+ // Unset archive credentials classify as missing-key skip, not failure.
93677
+ archive: { toolName: "archive_get_coverage", args: {} },
92942
93678
  fetch: {
92943
93679
  toolName: "fetch_public_api",
92944
93680
  args: { host: "api.coingecko.com", path: "/api/v3/ping" }
@@ -93042,6 +93778,8 @@ var FULL_MODE_DEFAULT_ARGS = {
93042
93778
  },
93043
93779
  // Keyless RWA venue reads: bound every full-mode probe.
93044
93780
  rwa: { limit: 10 },
93781
+ // Archive history reads: BTC exists in every dataset; keep probes bounded.
93782
+ archive: { base_coin: "BTC", limit: 5 },
93045
93783
  fetch: {
93046
93784
  host: "api.coingecko.com",
93047
93785
  path: "/api/v3/ping"
@@ -94389,6 +95127,7 @@ function getClientForProvider(provider, clients) {
94389
95127
  case "hyperliquid":
94390
95128
  case "fetch":
94391
95129
  case "defillama":
95130
+ case "archive":
94392
95131
  return null;
94393
95132
  case "codex":
94394
95133
  return null;
@@ -95149,14 +95888,17 @@ var TASK_CANARY_PROBES = [
95149
95888
  args: { query: "bitcoin" }
95150
95889
  }
95151
95890
  ];
95891
+ var PROBE_SKIPPED = /* @__PURE__ */ Symbol("probe_skipped");
95152
95892
  function classifyRuntimeStatus(probeStatus, error) {
95153
95893
  if (probeStatus === "passed") return "ok";
95154
- if (probeStatus === "skipped") return "degraded";
95894
+ if (probeStatus === "skipped") return PROBE_SKIPPED;
95155
95895
  return classifyRuntimeStatusFromError(error);
95156
95896
  }
95157
95897
  function aggregateTaskStatus(results) {
95158
95898
  if (results.length === 0) return "unknown";
95159
- return aggregateRuntimeStatus(results.map((result2) => result2.status));
95899
+ const observed = results.filter((result2) => !result2.skipped).map((result2) => result2.status);
95900
+ if (observed.length === 0) return "unknown";
95901
+ return aggregateRuntimeStatus(observed);
95160
95902
  }
95161
95903
  async function getTaskCanaryHistory(limit = 20) {
95162
95904
  const safeLimit = Math.max(1, Math.min(limit, TASK_CANARY_HISTORY_LIMIT));
@@ -95219,11 +95961,14 @@ async function runTaskCanary(toolsetId) {
95219
95961
  const toolset = TASK_TOOLSETS.find((item) => item.id === toolsetId);
95220
95962
  if (!probe || !toolset) return null;
95221
95963
  const result2 = await runSelfTestTool(probe.toolName, probe.args);
95222
- const status = classifyRuntimeStatus(result2.status, result2.error);
95964
+ const classified = classifyRuntimeStatus(result2.status, result2.error);
95965
+ const skipped = classified === PROBE_SKIPPED;
95966
+ const status = skipped ? "missing_key" : classified;
95223
95967
  return {
95224
95968
  toolsetId,
95225
95969
  label: toolset.label,
95226
95970
  status,
95971
+ ...skipped && { skipped: true },
95227
95972
  probeToolName: probe.toolName,
95228
95973
  provider: result2.provider,
95229
95974
  description: probe.description,
@@ -95234,20 +95979,57 @@ async function runTaskCanary(toolsetId) {
95234
95979
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
95235
95980
  };
95236
95981
  }
95982
+ var TASK_CANARY_PROBE_TIMEOUT_MS = Number.parseInt(
95983
+ process.env.HIVE_TASK_CANARY_TIMEOUT_MS || "10000",
95984
+ 10
95985
+ );
95986
+ var TASK_CANARY_CONCURRENCY = Number.parseInt(
95987
+ process.env.HIVE_TASK_CANARY_CONCURRENCY || "4",
95988
+ 10
95989
+ );
95990
+ async function runTaskCanaryBounded(probe) {
95991
+ if (TASK_CANARY_PROBE_TIMEOUT_MS <= 0) return runTaskCanary(probe.toolsetId);
95992
+ const startedAt = Date.now();
95993
+ let timer;
95994
+ const timeout = new Promise((resolve) => {
95995
+ timer = setTimeout(() => resolve("timeout"), TASK_CANARY_PROBE_TIMEOUT_MS);
95996
+ });
95997
+ try {
95998
+ const outcome = await Promise.race([runTaskCanary(probe.toolsetId), timeout]);
95999
+ if (outcome !== "timeout") return outcome;
96000
+ const toolset = TASK_TOOLSETS.find((item) => item.id === probe.toolsetId);
96001
+ return {
96002
+ toolsetId: probe.toolsetId,
96003
+ label: toolset?.label ?? probe.toolsetId,
96004
+ status: "degraded",
96005
+ probeToolName: probe.toolName,
96006
+ provider: "unknown",
96007
+ description: probe.description,
96008
+ duration: Date.now() - startedAt,
96009
+ error: `Canary exceeded ${TASK_CANARY_PROBE_TIMEOUT_MS}ms and was abandoned; the provider is slow or backing off rather than returning.`,
96010
+ requiredIdentifiers: toolset?.requiredIdentifiers ?? [],
96011
+ stableFixtures: toolset?.stableFixtures ?? [],
96012
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
96013
+ };
96014
+ } finally {
96015
+ if (timer) clearTimeout(timer);
96016
+ }
96017
+ }
95237
96018
  async function runTaskCanariesQuick() {
95238
96019
  const start = Date.now();
95239
- const results = [];
95240
96020
  const interProbeDelayMs = Number.parseInt(
95241
96021
  process.env.HIVE_TASK_CANARY_DELAY_MS || "350",
95242
96022
  10
95243
96023
  );
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
- }
96024
+ const settled = await mapWithConcurrency2(
96025
+ TASK_CANARY_PROBES,
96026
+ TASK_CANARY_CONCURRENCY,
96027
+ (probe) => runTaskCanaryBounded(probe),
96028
+ interProbeDelayMs
96029
+ );
96030
+ const results = settled.flatMap(
96031
+ (entry) => entry.status === "fulfilled" && entry.value ? [entry.value] : []
96032
+ );
95251
96033
  const observedAt = (/* @__PURE__ */ new Date()).toISOString();
95252
96034
  const summary = {
95253
96035
  ...getCurrentCanaryIdentity(observedAt),
@@ -97213,7 +97995,7 @@ var TASK_RESULT_VALIDATION_OUTPUT_SCHEMA = {
97213
97995
  warnings: { type: "array", items: { type: "string" } }
97214
97996
  }
97215
97997
  };
97216
- function errorResult3(message) {
97998
+ function errorResult4(message) {
97217
97999
  return {
97218
98000
  content: [
97219
98001
  {
@@ -97237,13 +98019,13 @@ function parseCursor(cursor) {
97237
98019
  const parsed = Number.parseInt(cursor, 10);
97238
98020
  return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
97239
98021
  }
97240
- function clampLimit3(limit, defaultLimit, maxLimit) {
98022
+ function clampLimit4(limit, defaultLimit, maxLimit) {
97241
98023
  if (!limit || !Number.isFinite(limit)) return defaultLimit;
97242
98024
  return Math.min(Math.max(Math.floor(limit), 1), maxLimit);
97243
98025
  }
97244
98026
  function paginate(items, cursor, limit, defaultLimit, maxLimit) {
97245
98027
  const offset = parseCursor(cursor);
97246
- const pageSize = clampLimit3(limit, defaultLimit, maxLimit);
98028
+ const pageSize = clampLimit4(limit, defaultLimit, maxLimit);
97247
98029
  const page = items.slice(offset, offset + pageSize);
97248
98030
  const nextOffset = offset + page.length;
97249
98031
  const hasMore = nextOffset < items.length;
@@ -97775,14 +98557,14 @@ function dynamicTools(endpoints, options = {}) {
97775
98557
  },
97776
98558
  handler: async (_client, args) => {
97777
98559
  if (!args) {
97778
- return errorResult3(
98560
+ return errorResult4(
97779
98561
  "No endpoint provided. Pass an 'endpoint' (or 'endpoint_name') parameter with the tool name."
97780
98562
  );
97781
98563
  }
97782
98564
  const parsedEndpointArgs = getEndpointSchema.parse(args);
97783
98565
  const requestedEndpoint = parsedEndpointArgs.endpoint ?? parsedEndpointArgs.endpoint_name;
97784
98566
  if (!requestedEndpoint) {
97785
- return errorResult3(
98567
+ return errorResult4(
97786
98568
  "No endpoint provided. Pass an 'endpoint' (or 'endpoint_name') parameter with the tool name."
97787
98569
  );
97788
98570
  }
@@ -97796,7 +98578,7 @@ function dynamicTools(endpoints, options = {}) {
97796
98578
  ...categoryTools
97797
98579
  ].find((endpoint2) => endpoint2.tool.name === requestedEndpoint);
97798
98580
  if (!lookup.ok && !metaEndpoint) {
97799
- return errorResult3(
98581
+ return errorResult4(
97800
98582
  `Endpoint '${lookup.resolvedToolName || lookup.requestedToolName}' not found. Use a category listing tool to discover available endpoints.`
97801
98583
  );
97802
98584
  }
@@ -97831,12 +98613,12 @@ function dynamicTools(endpoints, options = {}) {
97831
98613
  const invokeWithPolicy = async (invoker, client2, args) => {
97832
98614
  const { success, data, error } = invokeEndpointSchema.safeParse(args);
97833
98615
  if (!success) {
97834
- return errorResult3(
98616
+ return errorResult4(
97835
98617
  `Invalid arguments: ${JSON.stringify(error?.format())}`
97836
98618
  );
97837
98619
  }
97838
98620
  const invokeArgs = normalizeInvokeArgs(data);
97839
- if (!invokeArgs.ok) return errorResult3(invokeArgs.message);
98621
+ if (!invokeArgs.ok) return errorResult4(invokeArgs.message);
97840
98622
  const lookup = resolveEndpoint(endpoints, invokeArgs.resolvedToolName);
97841
98623
  if (!lookup.ok) {
97842
98624
  return createToolNotFoundResult(
@@ -97844,7 +98626,7 @@ function dynamicTools(endpoints, options = {}) {
97844
98626
  ).mcpResult;
97845
98627
  }
97846
98628
  if (!endpointAllowedForInvoker(invoker, lookup.endpoint)) {
97847
- return errorResult3(invokerPolicyMessage(invoker, lookup.endpoint));
98629
+ return errorResult4(invokerPolicyMessage(invoker, lookup.endpoint));
97848
98630
  }
97849
98631
  const executionContext = {
97850
98632
  transport: "dynamic",
@@ -97918,7 +98700,7 @@ function dynamicTools(endpoints, options = {}) {
97918
98700
  handler: async (_client, args) => {
97919
98701
  const parsed = validateTaskResultSchema.safeParse(args);
97920
98702
  if (!parsed.success) {
97921
- return errorResult3(
98703
+ return errorResult4(
97922
98704
  `Invalid task result validation request: ${JSON.stringify(parsed.error.format())}`
97923
98705
  );
97924
98706
  }
@@ -97926,7 +98708,7 @@ function dynamicTools(endpoints, options = {}) {
97926
98708
  (entry) => entry.id === parsed.data.task_toolset_id
97927
98709
  );
97928
98710
  if (!toolset) {
97929
- return errorResult3(
98711
+ return errorResult4(
97930
98712
  `Unknown task toolset '${parsed.data.task_toolset_id}'. Use search_tools to select a canonical id.`
97931
98713
  );
97932
98714
  }
@@ -99355,22 +100137,22 @@ function createHiveMcpServer(scope, options) {
99355
100137
 
99356
100138
  export {
99357
100139
  getEnvVar,
99358
- hasEnvVars,
99359
100140
  getCsvEnv,
99360
100141
  getNodeEnv,
99361
100142
  isProduction,
99362
100143
  getPort,
99363
100144
  getRateLimitMax,
100145
+ getCredentialedRateLimitMax,
99364
100146
  getCorsAllowedOrigins,
99365
100147
  isSupabaseConfigured,
99366
100148
  getSupabaseAdmin,
99367
- getSupabaseAuthClient,
99368
100149
  secureLogger,
99369
100150
  isB2BSubjectsEnabled,
99370
100151
  hasSubjectSigningSecret,
99371
100152
  buildSubjectBodyDigest,
99372
100153
  resolveSubjectContext,
99373
100154
  materializeSubjectContext,
100155
+ buildStatefulWriteAuditEvent,
99374
100156
  recordSubjectAuditEvent,
99375
100157
  runWithSubjectContext,
99376
100158
  HIVE_AUTH_USER_ARG,
@@ -99385,7 +100167,9 @@ export {
99385
100167
  trackOAuthFirstMaterialReceipt,
99386
100168
  getRedisClient,
99387
100169
  isRedisAvailable,
100170
+ waitForRedisAvailability,
99388
100171
  closeRedisClient,
100172
+ isRedisConfigured,
99389
100173
  PROVIDER_CATALOG,
99390
100174
  getProviderRuntimeStatus,
99391
100175
  READ_ENDPOINT_INVOKER,