hive-intelligence 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,9 +16,9 @@ through one MCP server and one CLI, and every answer carries a receipt:
16
16
  provider, fetch time, cache state, runtime status, and digests of the input
17
17
  and result. Three things Hive has that most crypto data tools do not:
18
18
 
19
- - **History that venues no longer serve.** Perp funding settlements, open
20
- interest, basis, long/short ratios, and liquidations for one coin across up
21
- to 33 CEX and DEX venues, archived since 2023-11.
19
+ - **History that venues no longer serve.** Perp funding settlements for one
20
+ coin across up to 33 CEX and DEX venues since 2023-11, plus basis, open
21
+ interest and liquidations since 2025 and long/short ratios since 2026-03.
22
22
  - **Cross-venue views.** One call compares funding or basis across venues
23
23
  instead of one exchange at a time.
24
24
  - **Provenance you can cite.** The `_hive` block on every material response
@@ -237,13 +237,15 @@ and probability claims are yours and must be labeled as such.
237
237
 
238
238
  Discovery tools are free; every other tool costs one credit. Keyed lanes debit
239
239
  only after validation and client resolution, so a validation error or a
240
- missing provider key costs 0 and a call that reaches the provider costs 1 even
241
- when the provider fails. The keyless lane consumes one allowance call per
242
- material request, valid or not. `report_feedback` is free on every lane.
240
+ missing provider key costs 0, and a call that fails after the debit is
241
+ refunded, so only a call that succeeds costs 1. The keyless lane takes one
242
+ allowance call per material request and gives it back when the call fails.
243
+ `report_feedback` is free on every lane.
243
244
 
244
245
  Every material response says what it cost: `credit_cost` (0 or 1),
245
- `credits_used` (what was actually debited), and `credits_remaining` (an
246
- integer, `null` when unlimited, absent when the lane has no notion of it).
246
+ `credits_used` (what the call cost, 0 when a failed call was refunded), and
247
+ `credits_remaining` (an integer, `null` when unlimited, absent when the lane
248
+ has no notion of it).
247
249
  Do not print the balance on every call; mention it when it is low or when the
248
250
  user asks.
249
251
 
@@ -27,10 +27,11 @@ file path and shape vary per client.
27
27
  interactive path is live.
28
28
  - **Cost** — discovery tools are free; every other tool costs one credit.
29
29
  Keyed lanes debit only after validation and client resolution, so a
30
- validation error or missing provider key costs 0 and a call that reaches the
31
- provider costs 1 even when the provider fails. The keyless lane consumes one
32
- allowance call per material request, valid or not. Every material response
33
- reports `credit_cost`, `credits_used`, and `credits_remaining`.
30
+ validation error or missing provider key costs 0, and a call that fails
31
+ after the debit is refunded, so only a call that succeeds costs 1. The
32
+ keyless lane takes one allowance call per material request and gives it back
33
+ when the call fails. Every material response reports `credit_cost`,
34
+ `credits_used`, and `credits_remaining`.
34
35
 
35
36
  After hosted OAuth activation, interactive MCP setup does not require the user
36
37
  to create or paste an API key. Route to `hive-build-onboarding` for direct CLI,
@@ -49029,7 +49029,14 @@ function getArchiveClient() {
49029
49029
  // src/tools/archive/api.ts
49030
49030
  var DEFAULT_LIMIT2 = 100;
49031
49031
  var MAX_LIMIT2 = 500;
49032
- var ARCHIVE_SINCE_NOTE = "archive since 2023-11";
49032
+ var ARCHIVE_SINCE = {
49033
+ funding: "2023-11",
49034
+ open_interest: "2025-04",
49035
+ basis: "2025-03",
49036
+ long_short: "2026-03",
49037
+ liquidations: "2025-04"
49038
+ };
49039
+ var ARCHIVE_SINCE_NOTE = `archive since ${ARCHIVE_SINCE.funding}`;
49033
49040
  var QUERY_TIMEOUT_MS = 1e4;
49034
49041
  var VENUE_SCAN_MAX_STEPS = 64;
49035
49042
  var COVERAGE_CACHE_TTL_MS = 10 * 60 * 1e3;
@@ -49323,7 +49330,7 @@ function historyHandler(toolName, dataset, options) {
49323
49330
  dataset: spec.table,
49324
49331
  runtime_status: "ok",
49325
49332
  fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
49326
- note: options?.extraNote ? `${ARCHIVE_SINCE_NOTE}. ${options.extraNote}` : ARCHIVE_SINCE_NOTE,
49333
+ note: options?.extraNote ? `archive since ${ARCHIVE_SINCE[dataset]}. ${options.extraNote}` : `archive since ${ARCHIVE_SINCE[dataset]}`,
49327
49334
  filters,
49328
49335
  order: `${spec.tsColumn} desc`,
49329
49336
  returnedCount: mapped.length,
@@ -54132,7 +54139,7 @@ var PROVIDER_CATALOG = [
54132
54139
  id: "archive",
54133
54140
  name: "Hive Archive",
54134
54141
  prefix: "archive_",
54135
- 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",
54142
+ type: "Cross-venue crypto derivatives history from Hive's own archive store: funding-rate settlements across 33 CEX+DEX venues since 2023-11, basis since 2025-03, open interest and liquidations since 2025-04, and long/short ratio since 2026-03",
54136
54143
  clientStrategy: "internal",
54137
54144
  credentialRequirement: {
54138
54145
  label: "SHARPE_ARCHIVE_SUPABASE_URL and SHARPE_ARCHIVE_SUPABASE_SERVICE_ROLE_KEY",
@@ -55266,7 +55273,7 @@ var ToolRegistry = [
55266
55273
  {
55267
55274
  category: "Market Data and Price",
55268
55275
  name: "get_market_and_price_endpoints",
55269
- 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 archive_ tools for cross-venue derivatives HISTORY (funding settlements, open interest, basis, long/short ratio, liquidations) since 2023-11.",
55276
+ 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 archive_ tools for cross-venue derivatives HISTORY: funding settlements since 2023-11; open interest, basis, long/short ratio and liquidations over shorter windows (archive_get_coverage gives exact depth).",
55270
55277
  tools: [
55271
55278
  "get_coins_index",
55272
55279
  "get_coins_market_data",
@@ -57665,6 +57672,27 @@ function createHiveExecutionMetadata(endpoint, durationMs, runtimeStatus, source
57665
57672
  function unchargedCredit(context) {
57666
57673
  return context.creditCost === void 0 ? {} : { creditCost: context.creditCost, creditsUsed: 0 };
57667
57674
  }
57675
+ var FAILED_CALL_REFUND_WARNING = "This call failed, so the credit it used was refunded.";
57676
+ async function refundFailedCall(billing, toolName, runtimeStatus, warnings) {
57677
+ if (!billing?.refund || billing.credits_used <= 0) return billing;
57678
+ try {
57679
+ const outcome = await billing.refund({ toolName, runtimeStatus });
57680
+ if (!outcome.refunded) return billing;
57681
+ warnings.push(FAILED_CALL_REFUND_WARNING);
57682
+ return {
57683
+ ...billing,
57684
+ credits_used: 0,
57685
+ ...outcome.creditsRemaining !== void 0 && {
57686
+ credits_remaining: outcome.creditsRemaining
57687
+ }
57688
+ };
57689
+ } catch (error) {
57690
+ secureLogger.error("Credit refund for a failed call failed", error, {
57691
+ toolName
57692
+ });
57693
+ return billing;
57694
+ }
57695
+ }
57668
57696
  function chargedCredit(context, billing) {
57669
57697
  if (context.creditCost === void 0 && !billing) return {};
57670
57698
  return {
@@ -58261,6 +58289,14 @@ async function executeEndpoint(endpoint, args, context) {
58261
58289
  );
58262
58290
  }
58263
58291
  }
58292
+ if (normalized.isError) {
58293
+ billing = await refundFailedCall(
58294
+ billing,
58295
+ endpoint.tool.name,
58296
+ runtimeStatus,
58297
+ guardWarnings
58298
+ );
58299
+ }
58264
58300
  const durationMs = Date.now() - start;
58265
58301
  const hive = createHiveExecutionMetadata(
58266
58302
  endpoint,
@@ -58340,12 +58376,21 @@ async function executeEndpoint(endpoint, args, context) {
58340
58376
  endpoint.tool.name,
58341
58377
  args
58342
58378
  );
58379
+ const thrownWarnings = [
58380
+ "Tool execution threw before returning a provider result."
58381
+ ];
58382
+ billing = await refundFailedCall(
58383
+ billing,
58384
+ endpoint.tool.name,
58385
+ runtimeStatus,
58386
+ thrownWarnings
58387
+ );
58343
58388
  const hive = createHiveExecutionMetadata(
58344
58389
  endpoint,
58345
58390
  durationMs,
58346
58391
  runtimeStatus,
58347
58392
  "unavailable",
58348
- ["Tool execution threw before returning a provider result."],
58393
+ thrownWarnings,
58349
58394
  void 0,
58350
58395
  {
58351
58396
  input: args ?? {},
@@ -7,7 +7,7 @@ import {
7
7
  getSupabaseAdmin,
8
8
  resolveEndpoint,
9
9
  secureLogger
10
- } from "./chunk-HWV3XFXH.js";
10
+ } from "./chunk-MVO6LL3H.js";
11
11
 
12
12
  // src/services/wallet-service.ts
13
13
  async function consumeCredits(args) {
@@ -34,6 +34,29 @@ async function consumeCredits(args) {
34
34
  cycleEnd: row.cycle_end ?? null
35
35
  };
36
36
  }
37
+ async function refundCredits(args) {
38
+ const { userId, debitIdempotencyKey, detail } = args;
39
+ const { data, error } = await getSupabaseAdmin().rpc("refund_credits", {
40
+ p_user_id: userId,
41
+ p_debit_idempotency_key: debitIdempotencyKey,
42
+ p_detail: detail ?? {}
43
+ });
44
+ if (error) {
45
+ secureLogger.error("refund_credits RPC failed", error, { userId });
46
+ throw new Error(`refund_credits failed: ${error.message}`);
47
+ }
48
+ const row = Array.isArray(data) ? data[0] : data;
49
+ if (!row) {
50
+ throw new Error("refund_credits returned no row");
51
+ }
52
+ return {
53
+ code: row.code,
54
+ balance: row.balance ?? null
55
+ };
56
+ }
57
+ function refundSettled(code) {
58
+ return code === "ok" || code === "unlimited" || code === "already_refunded";
59
+ }
37
60
  async function ensureWallet(userId, planId) {
38
61
  const { error } = await getSupabaseAdmin().rpc("ensure_wallet", {
39
62
  p_user_id: userId,
@@ -175,6 +198,8 @@ async function executeToolByName(toolName, args, options) {
175
198
 
176
199
  export {
177
200
  consumeCredits,
201
+ refundCredits,
202
+ refundSettled,
178
203
  ensureWallet,
179
204
  getWalletStatus,
180
205
  applyPlanChange,
package/build/cli.js CHANGED
@@ -3453,7 +3453,7 @@ program2.hook("preAction", (thisCommand) => {
3453
3453
  setCliFlags({ apiKey: opts.apiKey, noRetry: opts.retry === false });
3454
3454
  });
3455
3455
  program2.action(async () => {
3456
- const { startStdio } = await import("./serve-MOP4WYH6.js");
3456
+ const { startStdio } = await import("./serve-BCKB6DMI.js");
3457
3457
  await startStdio(program2.opts().envFile);
3458
3458
  });
3459
3459
  program2.command("serve").description("Start the MCP server").option("--http", "Start HTTP server instead of stdio").option("--port <number>", "HTTP server port", "8080").action(async (opts) => {
@@ -3466,10 +3466,10 @@ program2.command("serve").description("Start the MCP server").option("--http", "
3466
3466
  );
3467
3467
  process.exit(ExitCode.INVALID_ARGS);
3468
3468
  }
3469
- const { startHttp } = await import("./serve-MOP4WYH6.js");
3469
+ const { startHttp } = await import("./serve-BCKB6DMI.js");
3470
3470
  await startHttp(port, program2.opts().envFile);
3471
3471
  } else {
3472
- const { startStdio } = await import("./serve-MOP4WYH6.js");
3472
+ const { startStdio } = await import("./serve-BCKB6DMI.js");
3473
3473
  await startStdio(program2.opts().envFile);
3474
3474
  }
3475
3475
  });
@@ -3734,7 +3734,7 @@ program2.command("feedback <message>").description(
3734
3734
  await runFeedback(message, { tool: opts.tool, receipt: opts.receipt });
3735
3735
  });
3736
3736
  program2.command("doctor").description("Diagnose environment and server connectivity").option("--probe", "Run live API probes (slower)").action(async (opts) => {
3737
- const { runDoctor } = await import("./doctor-5HAAZU4R.js");
3737
+ const { runDoctor } = await import("./doctor-O2OQX6EX.js");
3738
3738
  await runDoctor(opts);
3739
3739
  });
3740
3740
  program2.command("upgrade").description("Update Hive to the latest published version").action(async () => {
@@ -3782,7 +3782,7 @@ program2.command("usage").description("Show your API usage and plan limits").act
3782
3782
  await runUsage();
3783
3783
  });
3784
3784
  program2.command("status").description("Quick health and version check").action(async () => {
3785
- const { runStatus } = await import("./doctor-5HAAZU4R.js");
3785
+ const { runStatus } = await import("./doctor-O2OQX6EX.js");
3786
3786
  await runStatus();
3787
3787
  });
3788
3788
  program2.command("open [target]").description("Open Hive dashboard, docs, status, or GitHub in browser").action(async (target) => {
@@ -240,7 +240,7 @@ ${fails === 0 ? "All checks passed" : `${fails} issues found`}${warns ? ` (${war
240
240
  if (fails > 0) process.exit(1);
241
241
  }
242
242
  async function runStatus() {
243
- const { SERVER_VERSION } = await import("./mcpServer-LNRRWXSV.js");
243
+ const { SERVER_VERSION } = await import("./mcpServer-FXQPSDDC.js");
244
244
  const { resolveApiKey, resolveApiUrl } = await import("./config-dir-2QLA5PXD.js");
245
245
  const { getActiveProfile, maskKey } = await import("./auth-6XVL5CE3.js");
246
246
  const apiUrl = resolveApiUrl();
@@ -9,7 +9,7 @@ import {
9
9
  getToolsForScope,
10
10
  memoizedJsonSchemaToZodInputSchema,
11
11
  memoizedJsonSchemaToZodOutputSchema
12
- } from "./chunk-HWV3XFXH.js";
12
+ } from "./chunk-MVO6LL3H.js";
13
13
  import "./chunk-4QDGA6GP.js";
14
14
  import "./chunk-EPF36Q3Z.js";
15
15
  import {
@@ -1,8 +1,10 @@
1
1
  import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
2
2
  import {
3
3
  consumeCredits,
4
- executeToolByName
5
- } from "./chunk-ORJIFYF4.js";
4
+ executeToolByName,
5
+ refundCredits,
6
+ refundSettled
7
+ } from "./chunk-WQE3B64R.js";
6
8
  import {
7
9
  HyperliquidAPI,
8
10
  MIN_MONITOR_CADENCE_MS,
@@ -15,7 +17,7 @@ import {
15
17
  monitorCadenceIntervalMs,
16
18
  runScheduledHealthCanaries,
17
19
  secureLogger
18
- } from "./chunk-HWV3XFXH.js";
20
+ } from "./chunk-MVO6LL3H.js";
19
21
  import "./chunk-4QDGA6GP.js";
20
22
  import "./chunk-EPF36Q3Z.js";
21
23
  import "./chunk-ZXKFJQDE.js";
@@ -640,6 +642,29 @@ async function defaultConsumeCredit(args) {
640
642
  requestId: args.runId
641
643
  });
642
644
  }
645
+ async function defaultRefundCredit(args) {
646
+ const refund = await refundCredits({
647
+ userId: args.userId,
648
+ debitIdempotencyKey: `monitor-run:${args.runId}`,
649
+ detail: { monitor_run: args.runId, reason: args.reason }
650
+ });
651
+ return refundSettled(refund.code);
652
+ }
653
+ async function refundMonitorRunCredit(args) {
654
+ try {
655
+ return await args.refundCredit({
656
+ userId: args.monitor.user_id,
657
+ runId: args.runId,
658
+ reason: args.reason
659
+ });
660
+ } catch (error) {
661
+ secureLogger.error("Monitor run credit refund failed", error, {
662
+ monitorId: args.monitor.id,
663
+ runId: args.runId
664
+ });
665
+ return false;
666
+ }
667
+ }
643
668
  async function debitMonitorRunCredit(args) {
644
669
  try {
645
670
  const debit = await args.consumeCredit({
@@ -976,6 +1001,7 @@ async function processMonitor(monitor, options) {
976
1001
  });
977
1002
  const completedAt = options.now.toISOString();
978
1003
  const nextRunAt = computeNextRunAt(monitor.cadence, options.now);
1004
+ let charged = false;
979
1005
  try {
980
1006
  const debit = await debitMonitorRunCredit({
981
1007
  monitor,
@@ -1007,6 +1033,7 @@ async function processMonitor(monitor, options) {
1007
1033
  alerts: 0
1008
1034
  };
1009
1035
  }
1036
+ charged = true;
1010
1037
  const previous = await options.store.getLatestSnapshot(monitor.id);
1011
1038
  const workflow = await executeMonitorWorkflow(
1012
1039
  monitor,
@@ -1101,6 +1128,13 @@ async function processMonitor(monitor, options) {
1101
1128
  });
1102
1129
  observations++;
1103
1130
  }
1131
+ const everyCallFailed = workflow.toolCalls.length > 0 && workflow.failedCalls.length === workflow.toolCalls.length;
1132
+ const creditRefunded = everyCallFailed ? await refundMonitorRunCredit({
1133
+ monitor,
1134
+ runId: run.id,
1135
+ reason: "every tool call failed",
1136
+ refundCredit: options.refundCredit
1137
+ }) : false;
1104
1138
  await options.store.finishRun({
1105
1139
  runId: run.id,
1106
1140
  status: "completed",
@@ -1109,7 +1143,8 @@ async function processMonitor(monitor, options) {
1109
1143
  metadata: {
1110
1144
  fingerprint: workflow.fingerprint,
1111
1145
  failed_call_count: workflow.failedCalls.length,
1112
- rule_evaluation_count: ruleEvaluations.length
1146
+ rule_evaluation_count: ruleEvaluations.length,
1147
+ ...creditRefunded && { credit_refunded: true }
1113
1148
  }
1114
1149
  });
1115
1150
  await options.store.releaseMonitor({
@@ -1129,6 +1164,14 @@ async function processMonitor(monitor, options) {
1129
1164
  };
1130
1165
  } catch (error) {
1131
1166
  const message = error instanceof Error ? error.message : String(error);
1167
+ if (charged) {
1168
+ await refundMonitorRunCredit({
1169
+ monitor,
1170
+ runId: run.id,
1171
+ reason: "run failed",
1172
+ refundCredit: options.refundCredit
1173
+ });
1174
+ }
1132
1175
  await options.store.finishRun({
1133
1176
  runId: run.id,
1134
1177
  status: "failed",
@@ -1165,6 +1208,7 @@ async function runDueMonitors(options = {}) {
1165
1208
  const now = options.now ?? /* @__PURE__ */ new Date();
1166
1209
  const executeTool = options.executeTool ?? executeToolByName;
1167
1210
  const consumeCredit = options.consumeCredit ?? defaultConsumeCredit;
1211
+ const refundCredit = options.refundCredit ?? defaultRefundCredit;
1168
1212
  const source = options.source ?? "scheduled";
1169
1213
  const claimed = await store.claimDueMonitors({
1170
1214
  batchSize,
@@ -1191,7 +1235,8 @@ async function runDueMonitors(options = {}) {
1191
1235
  now,
1192
1236
  source,
1193
1237
  executeTool,
1194
- consumeCredit
1238
+ consumeCredit,
1239
+ refundCredit
1195
1240
  })
1196
1241
  );
1197
1242
  }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "package": "hive-intelligence",
3
- "version": "1.8.0",
4
- "buildSha": "eac2d1b060973dfe826ff082d35708e2b80ac05c",
5
- "imageDigest": "sha256:cbc418fc3433b677d78aeef00b1b7b37ccaa440e00c6cab390ed327fd31f6669",
3
+ "version": "1.9.0",
4
+ "buildSha": "b157f6642120d33dd3c241c366731f0174a56902",
5
+ "imageDigest": "sha256:103576df07830e955af1f472dc39b365862c8b5a6965d2a944be363ea4e8b976",
6
6
  "deployedUrl": "https://mcp.hiveintelligence.xyz",
7
- "manifest": "https://raw.githubusercontent.com/hive-intel/hive-sdk/v1.8.0/releases/v1.8.0.json"
7
+ "manifest": "https://raw.githubusercontent.com/hive-intel/hive-sdk/v1.9.0/releases/v1.9.0.json"
8
8
  }
@@ -7,7 +7,7 @@ async function startStdio(envFile) {
7
7
  dotenv.config({ path: envFile || ".env", quiet: true });
8
8
  console.log = (...args) => console.error("[LOG]", ...args);
9
9
  const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
10
- const { createHiveMcpServer, SERVER_NAME, SERVER_VERSION } = await import("./mcpServer-LNRRWXSV.js");
10
+ const { createHiveMcpServer, SERVER_NAME, SERVER_VERSION } = await import("./mcpServer-FXQPSDDC.js");
11
11
  const { getPendingUpdateVersion } = await import("./update-check-36QU6PKA.js");
12
12
  const transport = new StdioServerTransport();
13
13
  const pendingUpdate = getPendingUpdateVersion();
package/build/server.js CHANGED
@@ -5,8 +5,10 @@ import {
5
5
  consumeCredits,
6
6
  ensureWallet,
7
7
  executeToolByName,
8
- getWalletStatus
9
- } from "./chunk-ORJIFYF4.js";
8
+ getWalletStatus,
9
+ refundCredits,
10
+ refundSettled
11
+ } from "./chunk-WQE3B64R.js";
10
12
  import {
11
13
  CategoryEndpoints,
12
14
  HERO_TOOL_NAMES,
@@ -102,7 +104,7 @@ import {
102
104
  validateEndpointArgs,
103
105
  verifyApiKey,
104
106
  waitForRedisAvailability
105
- } from "./chunk-HWV3XFXH.js";
107
+ } from "./chunk-MVO6LL3H.js";
106
108
  import {
107
109
  HIVE_AGENT_SKILLS
108
110
  } from "./chunk-4QDGA6GP.js";
@@ -2598,10 +2600,11 @@ async function debitApiKeyCreditForExecution(req, res) {
2598
2600
  const result = authReq.unkeyResult;
2599
2601
  if (!result || !authReq.userId) return;
2600
2602
  try {
2603
+ const idempotencyKey = authReq.creditDebitIdempotencyKey ?? randomUUID();
2601
2604
  const consumeResult = await consumeCredits({
2602
2605
  userId: authReq.userId,
2603
2606
  cost: REQUEST_COST,
2604
- idempotencyKey: authReq.creditDebitIdempotencyKey ?? randomUUID(),
2607
+ idempotencyKey,
2605
2608
  requestId: req.requestId,
2606
2609
  keyId: authReq.unkeyKeyId
2607
2610
  });
@@ -2627,6 +2630,7 @@ async function debitApiKeyCreditForExecution(req, res) {
2627
2630
  };
2628
2631
  }
2629
2632
  authReq.quotaRemaining = consumeResult.balance ?? void 0;
2633
+ authReq.creditDebitedKey = idempotencyKey;
2630
2634
  authReq.idempotencyExecutionStarted = true;
2631
2635
  res.setHeader(
2632
2636
  "X-Quota-Remaining",
@@ -5556,6 +5560,23 @@ async function consumeAnonMaterialQuota(ipHash, overrides = {}) {
5556
5560
  return { allowed: false, reason: "unavailable" };
5557
5561
  }
5558
5562
  }
5563
+ async function refundAnonMaterialQuota(ipHash) {
5564
+ try {
5565
+ if (!isSupabaseConfigured()) return void 0;
5566
+ const { data, error } = await getSupabaseAdmin().rpc(
5567
+ "refund_anon_daily_quota",
5568
+ { p_ip_hash: ipHash, p_ip_limit: getAnonDailyMaterialCap() }
5569
+ );
5570
+ if (error) throw new Error(error.message);
5571
+ const row = Array.isArray(data) ? data[0] : data;
5572
+ return typeof row?.remaining === "number" ? row.remaining : void 0;
5573
+ } catch (err) {
5574
+ secureLogger.warn("Anonymous quota refund failed", {
5575
+ error: err instanceof Error ? err.message : String(err)
5576
+ });
5577
+ return void 0;
5578
+ }
5579
+ }
5559
5580
 
5560
5581
  // src/serverCard.ts
5561
5582
  function buildServerCard() {
@@ -6925,10 +6946,11 @@ function buildMcpQuotaExhaustedCopy(args) {
6925
6946
  }
6926
6947
  async function debitMcpCredits(args) {
6927
6948
  try {
6949
+ const idempotencyKey = randomUUID4();
6928
6950
  const debit = await consumeCredits({
6929
6951
  userId: args.userId,
6930
6952
  cost: 1,
6931
- idempotencyKey: randomUUID4(),
6953
+ idempotencyKey,
6932
6954
  requestId: args.requestId,
6933
6955
  keyId: args.keyId
6934
6956
  });
@@ -6952,7 +6974,7 @@ async function debitMcpCredits(args) {
6952
6974
  rateLimited: true
6953
6975
  };
6954
6976
  }
6955
- return { ok: true, balance: debit.balance };
6977
+ return { ok: true, balance: debit.balance, idempotencyKey };
6956
6978
  } catch (err) {
6957
6979
  secureLogger.warn("MCP consume_credits debit failed", {
6958
6980
  error: err instanceof Error ? err.message : String(err)
@@ -6963,6 +6985,32 @@ async function debitMcpCredits(args) {
6963
6985
  };
6964
6986
  }
6965
6987
  }
6988
+ async function refundKeyedCredit(res, args) {
6989
+ const refund = await refundCredits({
6990
+ userId: args.userId,
6991
+ debitIdempotencyKey: args.debitIdempotencyKey,
6992
+ detail: {
6993
+ tool: args.toolName,
6994
+ runtime_status: args.runtimeStatus,
6995
+ transport: args.transport
6996
+ }
6997
+ });
6998
+ if (!refundSettled(refund.code)) return { refunded: false };
6999
+ if (!res.headersSent) {
7000
+ res.setHeader(
7001
+ "X-Quota-Remaining",
7002
+ refund.balance === null ? "unlimited" : String(refund.balance)
7003
+ );
7004
+ }
7005
+ return { refunded: true, creditsRemaining: refund.balance };
7006
+ }
7007
+ async function refundAnonCall(res, ipHash) {
7008
+ const remaining = await refundAnonMaterialQuota(ipHash);
7009
+ if (remaining !== void 0 && !res.headersSent) {
7010
+ res.setHeader("X-Quota-Remaining", String(remaining));
7011
+ }
7012
+ return remaining;
7013
+ }
6966
7014
  function extractApiKey(req) {
6967
7015
  return req.headers["x-api-key"] ?? (req.headers.authorization?.startsWith("Bearer ") ? req.headers.authorization.slice(7) : void 0);
6968
7016
  }
@@ -7412,10 +7460,10 @@ function createStatelessMcpHandler(scope) {
7412
7460
  if (!auth.ok && !mcpCredentialPresented(req)) {
7413
7461
  const decision = await authorizeAnonymousMcpRequest(req);
7414
7462
  if (decision.kind === "allow") {
7463
+ const ipHash = anonymousIpHash(ipRateLimitKeyFromRequest(req));
7415
7464
  anonymousLane = {
7416
- principalRaw: `anon:${anonymousIpHash(
7417
- ipRateLimitKeyFromRequest(req)
7418
- )}`,
7465
+ principalRaw: `anon:${ipHash}`,
7466
+ ipHash,
7419
7467
  materialGranted: decision.materialGranted,
7420
7468
  quotaRemaining: decision.quotaRemaining
7421
7469
  };
@@ -7641,6 +7689,8 @@ function createStatelessMcpHandler(scope) {
7641
7689
  clientName: resolveMcpClientName(req, body),
7642
7690
  isInternal: isMonitorSecretRequest(req)
7643
7691
  };
7692
+ let anonCallHeld = anonymousLane?.materialGranted === true;
7693
+ let anonCallBilled = false;
7644
7694
  const server = createHiveMcpServer(scope, {
7645
7695
  requestId: req.requestId,
7646
7696
  analytics,
@@ -7698,6 +7748,7 @@ function createStatelessMcpHandler(scope) {
7698
7748
  beforeToolExecution: keyedAuth?.userId ? async (executedToolName) => {
7699
7749
  const creditCost = getMcpCreditCost(executedToolName);
7700
7750
  let keyedBalance;
7751
+ let debitKey;
7701
7752
  res.setHeader("X-Hive-Credit-Cost", String(creditCost));
7702
7753
  if (creditCost > 0) {
7703
7754
  const debit = await debitMcpCredits({
@@ -7741,10 +7792,22 @@ function createStatelessMcpHandler(scope) {
7741
7792
  debit.balance === null ? "unlimited" : String(debit.balance)
7742
7793
  );
7743
7794
  keyedBalance = debit.balance;
7795
+ debitKey = debit.idempotencyKey;
7744
7796
  }
7797
+ const refund = debitKey ? (reason) => refundKeyedCredit(res, {
7798
+ userId: keyedAuth.userId,
7799
+ debitIdempotencyKey: debitKey,
7800
+ toolName: reason.toolName,
7801
+ runtimeStatus: reason.runtimeStatus,
7802
+ transport: "mcp"
7803
+ }) : void 0;
7745
7804
  if (subjectContext) {
7746
7805
  const materialized = await materializeSubjectContext(subjectContext);
7747
7806
  if (!materialized.ok) {
7807
+ await refund?.({
7808
+ toolName: executedToolName,
7809
+ runtimeStatus: "failing"
7810
+ }).catch(() => void 0);
7748
7811
  return {
7749
7812
  type: materialized.status === 403 ? "authentication_error" : "internal_error",
7750
7813
  code: materialized.code,
@@ -7768,7 +7831,8 @@ function createStatelessMcpHandler(scope) {
7768
7831
  return {
7769
7832
  type: "billing",
7770
7833
  credits_used: creditCost,
7771
- ...creditCost > 0 && { credits_remaining: keyedBalance }
7834
+ ...creditCost > 0 && { credits_remaining: keyedBalance },
7835
+ ...refund && { refund }
7772
7836
  };
7773
7837
  } : anonymousLane ? async (executedToolName) => {
7774
7838
  const creditCost = getMcpCreditCost(executedToolName);
@@ -7790,10 +7854,36 @@ function createStatelessMcpHandler(scope) {
7790
7854
  runtimeStatus: "failing"
7791
7855
  };
7792
7856
  }
7857
+ if (!anonCallHeld) {
7858
+ const quota = await consumeAnonMaterialQuota(
7859
+ anonymousLane.ipHash
7860
+ );
7861
+ if (!quota.allowed) {
7862
+ return {
7863
+ type: "rate_limit_error",
7864
+ code: "ANON_QUOTA_EXCEEDED",
7865
+ message: ANON_QUOTA_EXHAUSTED_MESSAGE,
7866
+ runtimeStatus: "rate_limited"
7867
+ };
7868
+ }
7869
+ anonCallHeld = true;
7870
+ anonymousLane.quotaRemaining = quota.remaining;
7871
+ }
7872
+ anonCallBilled = true;
7793
7873
  return {
7794
7874
  type: "billing",
7795
7875
  credits_used: creditCost,
7796
- credits_remaining: anonymousLane.quotaRemaining ?? null
7876
+ credits_remaining: anonymousLane.quotaRemaining ?? null,
7877
+ refund: async () => {
7878
+ const remaining = await refundAnonCall(
7879
+ res,
7880
+ anonymousLane.ipHash
7881
+ );
7882
+ if (remaining === void 0) return { refunded: false };
7883
+ anonCallHeld = false;
7884
+ anonymousLane.quotaRemaining = remaining;
7885
+ return { refunded: true, creditsRemaining: remaining };
7886
+ }
7797
7887
  };
7798
7888
  } : void 0
7799
7889
  }).getServer();
@@ -7820,6 +7910,10 @@ function createStatelessMcpHandler(scope) {
7820
7910
  () => transport.handleRequest(req, res, body)
7821
7911
  )
7822
7912
  );
7913
+ if (anonymousLane && anonCallHeld && !anonCallBilled) {
7914
+ anonCallHeld = false;
7915
+ await refundAnonCall(res, anonymousLane.ipHash);
7916
+ }
7823
7917
  secureLogger.debug("MCP request completed", {
7824
7918
  sessionId: sessionCorrelationId,
7825
7919
  scope: scope.kind === "root" ? "root" : `category:${scope.category}`
@@ -8341,6 +8435,7 @@ var anonymousHeroExecuteMiddleware = asyncHandler(
8341
8435
  return;
8342
8436
  }
8343
8437
  res.setHeader("X-Quota-Remaining", String(quota.remaining));
8438
+ let refundAttempted = false;
8344
8439
  const result = await executeEndpoint(heroEndpoint, body?.args ?? {}, {
8345
8440
  transport: "rest",
8346
8441
  requestId: req.requestId,
@@ -8349,7 +8444,12 @@ var anonymousHeroExecuteMiddleware = asyncHandler(
8349
8444
  beforeExecution: async () => ({
8350
8445
  type: "billing",
8351
8446
  credits_used: getMcpCreditCost(toolName),
8352
- credits_remaining: quota.remaining
8447
+ credits_remaining: quota.remaining,
8448
+ refund: async () => {
8449
+ refundAttempted = true;
8450
+ const remaining = await refundAnonCall(res, ipHash);
8451
+ return remaining === void 0 ? { refunded: false } : { refunded: true, creditsRemaining: remaining };
8452
+ }
8353
8453
  }),
8354
8454
  analytics: {
8355
8455
  principalRaw: `anon:${ipHash}`,
@@ -8358,6 +8458,9 @@ var anonymousHeroExecuteMiddleware = asyncHandler(
8358
8458
  isInternal: isMonitorSecretRequest(req)
8359
8459
  }
8360
8460
  });
8461
+ if (!result.ok && !refundAttempted) {
8462
+ await refundAnonCall(res, ipHash);
8463
+ }
8361
8464
  const meta = buildRestExecutionMeta(
8362
8465
  toolName,
8363
8466
  result.durationMs,
@@ -8413,6 +8516,14 @@ app.post(
8413
8516
  const endpointArgs = authReq.resolvedRestToolArgs ?? { ...args ?? {} };
8414
8517
  const keyedBeforeExecution = async (endpoint) => {
8415
8518
  const blocked = await debitApiKeyCreditForExecution(req, res);
8519
+ const debitKey = authReq.creditDebitedKey;
8520
+ const refund = debitKey && authReq.userId ? (reason) => refundKeyedCredit(res, {
8521
+ userId: authReq.userId,
8522
+ debitIdempotencyKey: debitKey,
8523
+ toolName: reason.toolName,
8524
+ runtimeStatus: reason.runtimeStatus,
8525
+ transport: "rest"
8526
+ }) : void 0;
8416
8527
  if (blocked) {
8417
8528
  if (blocked.code === "QUOTA_EXCEEDED") {
8418
8529
  recordPaywall(req, {
@@ -8431,6 +8542,10 @@ app.post(
8431
8542
  authReq.hiveSubjectContext
8432
8543
  );
8433
8544
  if (!materialized.ok) {
8545
+ await refund?.({
8546
+ toolName: endpoint.tool.name,
8547
+ runtimeStatus: "failing"
8548
+ }).catch(() => void 0);
8434
8549
  return {
8435
8550
  type: materialized.status === 403 ? "authentication_error" : "internal_error",
8436
8551
  code: materialized.code,
@@ -8458,7 +8573,8 @@ app.post(
8458
8573
  credits_used: creditCost,
8459
8574
  ...creditCost > 0 && remainingHeader !== void 0 && {
8460
8575
  credits_remaining: String(remainingHeader) === "unlimited" ? null : Number(remainingHeader)
8461
- }
8576
+ },
8577
+ ...refund && { refund }
8462
8578
  };
8463
8579
  };
8464
8580
  const heroEndpoint = HERO_TOOL_NAMES.has(executionToolName) ? getRestHeroEndpoint(executionToolName) : void 0;
package/build/stdio.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  import {
7
7
  createHiveMcpServer,
8
8
  secureLogger
9
- } from "./chunk-HWV3XFXH.js";
9
+ } from "./chunk-MVO6LL3H.js";
10
10
  import "./chunk-4QDGA6GP.js";
11
11
  import "./chunk-EPF36Q3Z.js";
12
12
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hive-intelligence",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Evidence-backed crypto due diligence for AI agents, with sources, freshness, and a runtime receipt on every call.",
5
5
  "mcpName": "xyz.hiveintelligence/mcp",
6
6
  "type": "module",