hive-intelligence 1.6.7 → 1.7.2

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 (57) hide show
  1. package/README.md +10 -4
  2. package/agent-skills/.claude-plugin/plugin.json +10 -0
  3. package/agent-skills/README.md +2 -0
  4. package/agent-skills/SKILL.md +18 -6
  5. package/agent-skills/evals/trigger-queries.json +70 -0
  6. package/agent-skills/package.json +3 -1
  7. package/agent-skills/packs/hive/SKILL.md +404 -0
  8. package/agent-skills/packs/hive/evals/evals.json +50 -0
  9. package/agent-skills/packs/hive/evals/triggers.json +39 -0
  10. package/agent-skills/packs/hive-build-onboarding/SKILL.md +1 -1
  11. package/agent-skills/packs/hive-cli/SKILL.md +2 -2
  12. package/agent-skills/packs/hive-mcp/SKILL.md +9 -5
  13. package/agent-skills/packs/hive-prediction-markets/SKILL.md +145 -0
  14. package/agent-skills/packs/hive-prediction-markets/evals/evals.json +29 -0
  15. package/agent-skills/packs/hive-prediction-markets/evals/triggers.json +14 -0
  16. package/agent-skills/packs/hive-query/SKILL.md +4 -2
  17. package/agent-skills/packs/hive-stateful-monitoring/SKILL.md +3 -3
  18. package/agent-skills/references/agents-routing.md +4 -1
  19. package/agent-skills/scripts/validate-agent-skills-package.mjs +2 -0
  20. package/agent-skills/skills.sh.json +9 -1
  21. package/build/{api-client-6AM3HVHQ.js → api-client-POBN6Y5S.js} +12 -3
  22. package/build/{auth-4LPSQJXD.js → auth-6XVL5CE3.js} +1 -1
  23. package/build/call-args-BUB6YNHW.js +27 -0
  24. package/build/{chunk-GADPH72D.js → chunk-4QDGA6GP.js} +52 -1
  25. package/build/{chunk-2SPCKQBJ.js → chunk-5QAXJON7.js} +71 -0
  26. package/build/chunk-APBLB62S.js +474 -0
  27. package/build/chunk-AZFOTKS2.js +394 -0
  28. package/build/{chunk-GYVF75XB.js → chunk-FQNW6SZ6.js} +1 -1
  29. package/build/{chunk-32NRUWXA.js → chunk-KQVPKTEO.js} +3 -3
  30. package/build/{chunk-LW2DHE32.js → chunk-L4KVJDA7.js} +1 -1
  31. package/build/{chunk-J2PXISXW.js → chunk-LP7PDQCO.js} +4289 -626
  32. package/build/{chunk-P7NLFSZQ.js → chunk-RNVZIPNY.js} +100 -9
  33. package/build/chunk-XN3T6YOG.js +32 -0
  34. package/build/{chunk-KUF754NT.js → chunk-YLHNUAFT.js} +52 -12
  35. package/build/{chunk-G23ZAW36.js → chunk-ZBMMJ6LE.js} +3 -2
  36. package/build/{chunk-LP5ECG7A.js → chunk-ZDXMZPC6.js} +26 -2
  37. package/build/{chunk-S4HBVZ7O.js → chunk-ZSPPJW4G.js} +44 -7
  38. package/build/cli.js +120 -48
  39. package/build/{completion-YVSWWF6D.js → completion-ROZAH667.js} +1 -1
  40. package/build/{doctor-AM6QST7Z.js → doctor-KPYW2U7D.js} +42 -5
  41. package/build/{errors-EL6UMXNJ.js → errors-YQAVCXXO.js} +9 -1
  42. package/build/feedback-G2DFCCI6.js +113 -0
  43. package/build/{init-all-54G3CVZA.js → init-all-JXLG7LOX.js} +5 -5
  44. package/build/{mcpServer-NWOKFR7Q.js → mcpServer-SUDGFNIF.js} +2 -2
  45. package/build/monitor-worker.js +62 -10
  46. package/build/{namespace-PA2IDNRT.js → namespace-XMFFW47T.js} +1 -1
  47. package/build/{output-XGTE4EWJ.js → output-BBHBJI32.js} +5 -1
  48. package/build/release.json +4 -4
  49. package/build/routing-4VKAVRYR.js +43 -0
  50. package/build/{serve-XRNMHIJX.js → serve-BYC3LRLN.js} +1 -1
  51. package/build/server.js +317 -41
  52. package/build/stdio.js +2 -2
  53. package/build/{tools-FNPMELSU.js → tools-NJQPIJ53.js} +81 -33
  54. package/build/{uninstall-P3JJZCXT.js → uninstall-4TJ4SZLU.js} +26 -7
  55. package/build/{upgrade-ATIFZANV.js → upgrade-3NR3LAGU.js} +2 -2
  56. package/build/{watch-FG2C5Y2Z.js → watch-BUSEPVDM.js} +5 -3
  57. package/package.json +1 -1
package/build/server.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  ensureWallet,
7
7
  executeToolByName,
8
8
  getWalletStatus
9
- } from "./chunk-G23ZAW36.js";
9
+ } from "./chunk-ZBMMJ6LE.js";
10
10
  import {
11
11
  CategoryEndpoints,
12
12
  HERO_TOOL_NAMES,
@@ -34,7 +34,9 @@ import {
34
34
  createHiveMcpServer,
35
35
  createOAuthStore,
36
36
  createToolNotFoundResult,
37
+ describeErrorCode,
37
38
  endpointAllowedForInvoker,
39
+ errorDocUrl,
38
40
  executeEndpoint,
39
41
  getAllWrappedEndpoints,
40
42
  getAnalyticsWriter,
@@ -64,6 +66,7 @@ import {
64
66
  heroTools,
65
67
  isB2BSubjectsEnabled,
66
68
  isEndpointInvokerName,
69
+ isFreeWriteEndpoint,
67
70
  isHiveOAuthAccessToken,
68
71
  isProduction,
69
72
  isRedisAvailable,
@@ -74,6 +77,7 @@ import {
74
77
  listKeys,
75
78
  materializeSubjectContext,
76
79
  normalizeInvokeArgs,
80
+ principalKeyFromRaw,
77
81
  recordProviderCanary,
78
82
  recordSubjectAuditEvent,
79
83
  resolveEndpoint,
@@ -87,19 +91,21 @@ import {
87
91
  runProviderCanaryQuick,
88
92
  runTaskCanariesQuick,
89
93
  runTaskCanary,
94
+ runWithRequestPrincipal,
90
95
  runWithSubjectContext,
91
96
  runWorkflowRegressionCanaries,
92
97
  secureLogger,
98
+ submitFeedback,
93
99
  trackCliCommand,
94
100
  trackOAuthFirstMaterialReceipt,
95
101
  trackServerEvent,
96
102
  validateEndpointArgs,
97
103
  verifyApiKey,
98
104
  waitForRedisAvailability
99
- } from "./chunk-J2PXISXW.js";
105
+ } from "./chunk-LP7PDQCO.js";
100
106
  import {
101
107
  HIVE_AGENT_SKILLS
102
- } from "./chunk-GADPH72D.js";
108
+ } from "./chunk-4QDGA6GP.js";
103
109
  import "./chunk-EPF36Q3Z.js";
104
110
  import {
105
111
  PUBLIC_MCP_REGISTRY_ID,
@@ -762,20 +768,11 @@ import rateLimit from "express-rate-limit";
762
768
  import RedisStore from "rate-limit-redis";
763
769
 
764
770
  // src/utils/api-errors.ts
765
- var ERROR_DOC_BASE = "https://www.hiveintelligence.xyz/errors";
766
- var ERROR_DOC_URLS = {
767
- MISSING_PARAM: `${ERROR_DOC_BASE}#missing-param`,
768
- TOOL_NOT_FOUND: `${ERROR_DOC_BASE}#tool-not-found`,
769
- TOOL_ERROR: `${ERROR_DOC_BASE}#tool-error`,
770
- PROVIDER_UNAVAILABLE: `${ERROR_DOC_BASE}#provider-unavailable`,
771
- RATE_LIMITED: `${ERROR_DOC_BASE}#rate-limited`,
772
- INTERNAL_ERROR: `${ERROR_DOC_BASE}#internal-error`,
773
- ENDPOINT_NOT_FOUND: `${ERROR_DOC_BASE}#endpoint-not-found`,
774
- SHUTTING_DOWN: `${ERROR_DOC_BASE}#shutting-down`,
775
- UNAUTHORIZED: `${ERROR_DOC_BASE}#unauthorized`,
776
- VALIDATION_ERROR: `${ERROR_DOC_BASE}#validation-error`
777
- };
778
771
  function apiError(type, code, message, opts) {
772
+ const known = describeErrorCode(code);
773
+ const cause = opts?.cause ?? known?.meaning;
774
+ const nextAction = opts?.next_action ?? known?.nextAction;
775
+ const retryable = opts?.retryable ?? known?.retryable;
779
776
  return {
780
777
  ok: false,
781
778
  error: {
@@ -784,7 +781,11 @@ function apiError(type, code, message, opts) {
784
781
  message,
785
782
  ...opts?.param && { param: opts.param },
786
783
  ...opts?.requestId && { request_id: opts.requestId },
787
- doc_url: ERROR_DOC_URLS[code] ?? `${ERROR_DOC_BASE}#${code.toLowerCase().replace(/_/g, "-")}`
784
+ ...cause && { cause },
785
+ ...nextAction && { next_action: nextAction },
786
+ ...retryable !== void 0 && { retryable },
787
+ ...opts?.retry_after !== void 0 && { retry_after: opts.retry_after },
788
+ doc_url: errorDocUrl(code)
788
789
  }
789
790
  };
790
791
  }
@@ -7191,13 +7192,24 @@ function classifyAnonymousMcpRequest(body) {
7191
7192
  return "auth_required";
7192
7193
  }
7193
7194
  if (exposedToolName.startsWith("hive_")) return "auth_required";
7195
+ if (isFreeWriteEndpoint(exposedToolName)) return "open";
7194
7196
  if (isEndpointInvokerName(exposedToolName)) {
7195
7197
  const target = getInvokeEndpointName(body?.params?.arguments);
7198
+ if (target && isFreeWriteEndpoint(target)) return "open";
7196
7199
  if (target?.startsWith("hive_")) return "auth_required";
7197
7200
  return "material";
7198
7201
  }
7199
7202
  return getMcpCreditCost(exposedToolName) === 0 ? "open" : "material";
7200
7203
  }
7204
+ function anonymousPaywallToolName(body) {
7205
+ const params = body?.params;
7206
+ const name = typeof params?.name === "string" ? params.name : void 0;
7207
+ if (name !== "invoke_api_endpoint" && name !== "invoke_stateful_endpoint") {
7208
+ return name;
7209
+ }
7210
+ const target = params?.arguments?.endpoint;
7211
+ return typeof target === "string" ? target : name;
7212
+ }
7201
7213
  async function authorizeAnonymousMcpRequest(req) {
7202
7214
  const access = classifyAnonymousMcpRequest(req.body);
7203
7215
  if (access === "auth_required") return { kind: "require_auth" };
@@ -7413,6 +7425,13 @@ function createStatelessMcpHandler(scope) {
7413
7425
  } else if (decision.kind === "quota_exhausted") {
7414
7426
  res.setHeader("Retry-After", String(decision.resetSeconds));
7415
7427
  const exhaustedMessage = decision.scope === "global" ? ANON_GLOBAL_CAP_MESSAGE : ANON_QUOTA_EXHAUSTED_MESSAGE;
7428
+ const exhaustedCode = decision.scope === "global" ? "ANON_GLOBAL_CAP_EXCEEDED" : "ANON_QUOTA_EXCEEDED";
7429
+ recordPaywall(req, {
7430
+ code: exhaustedCode,
7431
+ authLane: "anonymous",
7432
+ transport: "mcp",
7433
+ toolName: req.body?.params?.name
7434
+ });
7416
7435
  if (mcpOAuth.status === "ready") {
7417
7436
  const requiredScopes = getRequiredMcpScopes(req.body);
7418
7437
  res.setHeader(
@@ -7434,9 +7453,12 @@ function createStatelessMcpHandler(scope) {
7434
7453
  code: -32001,
7435
7454
  message: exhaustedMessage,
7436
7455
  data: {
7437
- code: decision.scope === "global" ? "ANON_GLOBAL_CAP_EXCEEDED" : "ANON_QUOTA_EXCEEDED",
7456
+ code: exhaustedCode,
7438
7457
  retryable: true,
7439
- retry_after: decision.resetSeconds
7458
+ retry_after: decision.resetSeconds,
7459
+ cause: decision.scope === "global" ? "Hive's shared keyless allowance for today is spent." : "This IP's 25 free material calls for today are spent.",
7460
+ next_action: "Set HIVE_API_KEY (free at https://hiveintelligence.xyz/dashboard/keys) or sign in via OAuth; keys are not subject to the keyless cap.",
7461
+ doc_url: errorDocUrl(exhaustedCode)
7440
7462
  }
7441
7463
  },
7442
7464
  id: req.body?.id ?? null
@@ -7458,6 +7480,13 @@ function createStatelessMcpHandler(scope) {
7458
7480
  id: req.body?.id ?? null
7459
7481
  });
7460
7482
  return;
7483
+ } else if (decision.kind === "require_auth") {
7484
+ recordPaywall(req, {
7485
+ code: "ANON_AUTH_REQUIRED",
7486
+ authLane: "anonymous",
7487
+ transport: "mcp",
7488
+ toolName: anonymousPaywallToolName(req.body)
7489
+ });
7461
7490
  }
7462
7491
  }
7463
7492
  if (!auth.ok && !anonymousLane) {
@@ -7668,6 +7697,7 @@ function createStatelessMcpHandler(scope) {
7668
7697
  } : void 0,
7669
7698
  beforeToolExecution: keyedAuth?.userId ? async (executedToolName) => {
7670
7699
  const creditCost = getMcpCreditCost(executedToolName);
7700
+ let keyedBalance;
7671
7701
  res.setHeader("X-Hive-Credit-Cost", String(creditCost));
7672
7702
  if (creditCost > 0) {
7673
7703
  const debit = await debitMcpCredits({
@@ -7687,7 +7717,16 @@ function createStatelessMcpHandler(scope) {
7687
7717
  next_action: "Visit https://hiveintelligence.xyz/dashboard to initialize billing, or contact support. Retrying will not help until the wallet exists."
7688
7718
  };
7689
7719
  }
7690
- if (debit.rateLimited) res.setHeader("Retry-After", "60");
7720
+ if (debit.rateLimited) {
7721
+ res.setHeader("Retry-After", "60");
7722
+ recordPaywall(req, {
7723
+ code: "QUOTA_EXCEEDED",
7724
+ authLane: keyedAuth.authMethod,
7725
+ transport: "mcp",
7726
+ principalRaw: keyedAuth.userId,
7727
+ toolName: executedToolName
7728
+ });
7729
+ }
7691
7730
  return {
7692
7731
  type: debit.rateLimited ? "rate_limit_error" : "internal_error",
7693
7732
  code: debit.rateLimited ? "QUOTA_EXCEEDED" : "QUOTA_SERVICE_UNAVAILABLE",
@@ -7701,6 +7740,7 @@ function createStatelessMcpHandler(scope) {
7701
7740
  "X-Quota-Remaining",
7702
7741
  debit.balance === null ? "unlimited" : String(debit.balance)
7703
7742
  );
7743
+ keyedBalance = debit.balance;
7704
7744
  }
7705
7745
  if (subjectContext) {
7706
7746
  const materialized = await materializeSubjectContext(subjectContext);
@@ -7725,12 +7765,24 @@ function createStatelessMcpHandler(scope) {
7725
7765
  requestId: req.requestId
7726
7766
  });
7727
7767
  if (auditEvent) recordSubjectAuditEvent(auditEvent);
7728
- return void 0;
7768
+ return {
7769
+ type: "billing",
7770
+ credits_used: creditCost,
7771
+ ...creditCost > 0 && { credits_remaining: keyedBalance }
7772
+ };
7729
7773
  } : anonymousLane ? async (executedToolName) => {
7730
7774
  const creditCost = getMcpCreditCost(executedToolName);
7731
7775
  res.setHeader("X-Hive-Credit-Cost", String(creditCost));
7732
- if (creditCost === 0) return void 0;
7776
+ if (creditCost === 0) {
7777
+ return { type: "billing", credits_used: 0 };
7778
+ }
7733
7779
  if (!anonymousLane.materialGranted || executedToolName.startsWith("hive_")) {
7780
+ recordPaywall(req, {
7781
+ code: "ANON_AUTH_REQUIRED",
7782
+ authLane: "anonymous",
7783
+ transport: "mcp",
7784
+ toolName: executedToolName
7785
+ });
7734
7786
  return {
7735
7787
  type: "authentication_error",
7736
7788
  code: "ANON_AUTH_REQUIRED",
@@ -7738,7 +7790,11 @@ function createStatelessMcpHandler(scope) {
7738
7790
  runtimeStatus: "failing"
7739
7791
  };
7740
7792
  }
7741
- return void 0;
7793
+ return {
7794
+ type: "billing",
7795
+ credits_used: creditCost,
7796
+ credits_remaining: anonymousLane.quotaRemaining ?? null
7797
+ };
7742
7798
  } : void 0
7743
7799
  }).getServer();
7744
7800
  const transport = new StreamableHTTPServerTransport({
@@ -7752,7 +7808,17 @@ function createStatelessMcpHandler(scope) {
7752
7808
  await server.connect(transport);
7753
7809
  await runWithSubjectContext(
7754
7810
  subjectContext,
7755
- () => transport.handleRequest(req, res, body)
7811
+ () => runWithRequestPrincipal(
7812
+ analytics.principalRaw && analytics.authLane ? {
7813
+ key: principalKeyFromRaw(
7814
+ analytics.principalRaw,
7815
+ analytics.authLane
7816
+ ),
7817
+ lane: analytics.authLane,
7818
+ client: analytics.clientName ?? null
7819
+ } : void 0,
7820
+ () => transport.handleRequest(req, res, body)
7821
+ )
7756
7822
  );
7757
7823
  secureLogger.debug("MCP request completed", {
7758
7824
  sessionId: sessionCorrelationId,
@@ -7968,6 +8034,161 @@ var REST_EXECUTE_STATUS_CODES = {
7968
8034
  STATE_SUBJECT_ARCHIVED: 410,
7969
8035
  STATE_BACKEND_UNAVAILABLE: 503
7970
8036
  };
8037
+ function restErrorOptionsFromReceipt(meta) {
8038
+ return {
8039
+ ...meta.cause && { cause: meta.cause },
8040
+ ...meta.next_action && { next_action: meta.next_action },
8041
+ retryable: meta.runtime_status === "rate_limited" || meta.runtime_status === "degraded" || meta.runtime_status === "failing"
8042
+ };
8043
+ }
8044
+ function recordPaywall(req, event) {
8045
+ try {
8046
+ getAnalyticsWriter().enqueuePaywall({
8047
+ code: event.code,
8048
+ authLane: event.authLane,
8049
+ transport: event.transport,
8050
+ principalRaw: event.principalRaw ?? `anon:${anonymousIpHash(ipRateLimitKeyFromRequest(req))}`,
8051
+ toolName: typeof event.toolName === "string" ? event.toolName : null,
8052
+ isInternal: isMonitorSecretRequest(req)
8053
+ });
8054
+ } catch {
8055
+ }
8056
+ }
8057
+ async function resolveOptionalRestPrincipal(req) {
8058
+ if (mcpCredentialPresented(req)) {
8059
+ const auth = await validateMcpCredential(req);
8060
+ if (!auth.ok) {
8061
+ return {
8062
+ ok: false,
8063
+ status: 401,
8064
+ code: "UNAUTHORIZED",
8065
+ message: "The credential presented was rejected."
8066
+ };
8067
+ }
8068
+ const principalId = auth.userId ?? auth.keyId ?? "static";
8069
+ return {
8070
+ ok: true,
8071
+ key: `user:${principalId}`,
8072
+ lane: auth.authMethod,
8073
+ userId: auth.userId
8074
+ };
8075
+ }
8076
+ return {
8077
+ ok: true,
8078
+ key: `anon:${anonymousIpHash(ipRateLimitKeyFromRequest(req))}`,
8079
+ lane: "anonymous"
8080
+ };
8081
+ }
8082
+ app.post("/api/v1/feedback", async (req, res) => {
8083
+ const principal = await resolveOptionalRestPrincipal(req);
8084
+ if (!principal.ok) {
8085
+ res.status(principal.status).json(
8086
+ apiError("authentication_error", principal.code, principal.message, {
8087
+ requestId: req.requestId
8088
+ })
8089
+ );
8090
+ return;
8091
+ }
8092
+ const body = req.body ?? {};
8093
+ const result = await submitFeedback({
8094
+ principalKey: principal.key,
8095
+ lane: principal.lane,
8096
+ client: typeof body.client === "string" ? body.client : null,
8097
+ message: body.message,
8098
+ tool: body.tool,
8099
+ receiptId: body.receipt_id
8100
+ });
8101
+ if (result.ok) {
8102
+ res.json({
8103
+ ok: true,
8104
+ data: { id: result.id, remaining_today: result.remaining, reset_at: result.resetAt }
8105
+ });
8106
+ return;
8107
+ }
8108
+ const status = result.code === "VALIDATION_ERROR" ? 400 : result.code === "FEEDBACK_RATE_LIMITED" ? 429 : 503;
8109
+ if (result.resetAt) {
8110
+ const seconds = Math.max(
8111
+ 1,
8112
+ Math.round((Date.parse(result.resetAt) - Date.now()) / 1e3)
8113
+ );
8114
+ res.setHeader("Retry-After", String(seconds));
8115
+ }
8116
+ res.status(status).json({
8117
+ ...apiError(
8118
+ result.code === "VALIDATION_ERROR" ? "invalid_request_error" : result.code === "FEEDBACK_RATE_LIMITED" ? "rate_limit_error" : "internal_error",
8119
+ result.code,
8120
+ result.message,
8121
+ {
8122
+ requestId: req.requestId,
8123
+ retryable: result.retryable,
8124
+ ...result.resetAt && {
8125
+ next_action: `Retry after ${result.resetAt}.`
8126
+ }
8127
+ }
8128
+ ),
8129
+ ...result.resetAt && { reset_at: result.resetAt }
8130
+ });
8131
+ });
8132
+ app.get("/api/v1/quota", async (req, res) => {
8133
+ const principal = await resolveOptionalRestPrincipal(req);
8134
+ if (!principal.ok) {
8135
+ res.status(principal.status).json(
8136
+ apiError("authentication_error", principal.code, principal.message, {
8137
+ requestId: req.requestId
8138
+ })
8139
+ );
8140
+ return;
8141
+ }
8142
+ try {
8143
+ if (principal.lane === "anonymous") {
8144
+ if (!isSupabaseConfigured()) {
8145
+ res.json({
8146
+ ok: true,
8147
+ data: { lane: "anonymous", remaining: null, limit: getAnonDailyMaterialCap(), reset_at: null }
8148
+ });
8149
+ return;
8150
+ }
8151
+ const { data, error } = await getSupabaseAdmin().rpc(
8152
+ "read_anon_daily_quota",
8153
+ {
8154
+ p_ip_hash: anonymousIpHash(ipRateLimitKeyFromRequest(req)),
8155
+ p_ip_limit: getAnonDailyMaterialCap()
8156
+ }
8157
+ );
8158
+ if (error) throw new Error(error.message);
8159
+ const row = Array.isArray(data) ? data[0] : data;
8160
+ const resetSeconds = Math.max(1, row?.reset_seconds ?? 1);
8161
+ res.json({
8162
+ ok: true,
8163
+ data: {
8164
+ lane: "anonymous",
8165
+ remaining: row?.remaining ?? null,
8166
+ limit: row?.limit_value ?? getAnonDailyMaterialCap(),
8167
+ reset_at: new Date(Date.now() + resetSeconds * 1e3).toISOString()
8168
+ }
8169
+ });
8170
+ return;
8171
+ }
8172
+ const wallet = principal.userId ? await getWalletStatus(principal.userId) : null;
8173
+ res.json({
8174
+ ok: true,
8175
+ data: {
8176
+ lane: principal.lane,
8177
+ remaining: wallet ? wallet.balance : null,
8178
+ limit: wallet ? wallet.monthlyGrant : null,
8179
+ reset_at: wallet ? wallet.cycleEnd : null
8180
+ }
8181
+ });
8182
+ } catch (error) {
8183
+ secureLogger.error("Quota read failed", error, { requestId: req.requestId });
8184
+ res.status(503).json(
8185
+ apiError("internal_error", "QUOTA_SERVICE_UNAVAILABLE", "The quota read is unavailable right now.", {
8186
+ requestId: req.requestId,
8187
+ retryable: true
8188
+ })
8189
+ );
8190
+ }
8191
+ });
7971
8192
  function buildRestExecutionMeta(toolName, durationMs, meta) {
7972
8193
  return {
7973
8194
  tool: toolName,
@@ -7990,6 +8211,14 @@ function buildRestExecutionMeta(toolName, durationMs, meta) {
7990
8211
  origin_source: meta.origin_source,
7991
8212
  truncated: meta.truncated,
7992
8213
  warnings: meta.warnings,
8214
+ ...meta.credit_cost !== void 0 && { credit_cost: meta.credit_cost },
8215
+ ...meta.credits_used !== void 0 && { credits_used: meta.credits_used },
8216
+ ...meta.credits_remaining !== void 0 && {
8217
+ credits_remaining: meta.credits_remaining
8218
+ },
8219
+ ...meta.cause && { cause: meta.cause },
8220
+ ...meta.next_action && { next_action: meta.next_action },
8221
+ ...meta.doc_url && { doc_url: meta.doc_url },
7993
8222
  // Agent-actionable copy travels on every transport, not only MCP: REST and
7994
8223
  // CLI callers were getting "not found" with no replacement (2026-09-14).
7995
8224
  ...meta.cause !== void 0 && { cause: meta.cause },
@@ -8086,13 +8315,27 @@ var anonymousHeroExecuteMiddleware = asyncHandler(
8086
8315
  return;
8087
8316
  }
8088
8317
  const globalCap = quota.reason === "global_cap";
8318
+ const capCode = globalCap ? "ANON_GLOBAL_CAP_EXCEEDED" : "ANON_QUOTA_EXCEEDED";
8319
+ recordPaywall(req, {
8320
+ code: capCode,
8321
+ authLane: "anonymous",
8322
+ transport: "rest",
8323
+ principalRaw: `anon:${ipHash}`,
8324
+ toolName
8325
+ });
8089
8326
  res.setHeader("Retry-After", String(quota.resetSeconds));
8090
8327
  res.status(429).json(
8091
8328
  apiError(
8092
8329
  "rate_limit_error",
8093
- globalCap ? "ANON_GLOBAL_CAP_EXCEEDED" : "ANON_QUOTA_EXCEEDED",
8330
+ capCode,
8094
8331
  globalCap ? ANON_GLOBAL_CAP_MESSAGE : ANON_QUOTA_EXHAUSTED_MESSAGE,
8095
- { requestId: req.requestId }
8332
+ {
8333
+ requestId: req.requestId,
8334
+ cause: globalCap ? "Hive's shared keyless allowance for today is spent." : "This IP's free material calls for today are spent.",
8335
+ next_action: "Set HIVE_API_KEY (free at https://hiveintelligence.xyz/dashboard/keys); keys are not subject to the keyless cap.",
8336
+ retryable: true,
8337
+ retry_after: quota.resetSeconds
8338
+ }
8096
8339
  )
8097
8340
  );
8098
8341
  return;
@@ -8102,6 +8345,12 @@ var anonymousHeroExecuteMiddleware = asyncHandler(
8102
8345
  transport: "rest",
8103
8346
  requestId: req.requestId,
8104
8347
  clients: getSharedClients(),
8348
+ creditCost: getMcpCreditCost(toolName),
8349
+ beforeExecution: async () => ({
8350
+ type: "billing",
8351
+ credits_used: getMcpCreditCost(toolName),
8352
+ credits_remaining: quota.remaining
8353
+ }),
8105
8354
  analytics: {
8106
8355
  principalRaw: `anon:${ipHash}`,
8107
8356
  authLane: "anonymous",
@@ -8125,7 +8374,8 @@ var anonymousHeroExecuteMiddleware = asyncHandler(
8125
8374
  };
8126
8375
  res.status(REST_EXECUTE_STATUS_CODES[error.code] ?? 500).json({
8127
8376
  ...apiError(error.type, error.code, error.message, {
8128
- requestId: req.requestId
8377
+ requestId: req.requestId,
8378
+ ...restErrorOptionsFromReceipt(result.hive)
8129
8379
  }),
8130
8380
  meta,
8131
8381
  ...result.validationErrors && {
@@ -8163,7 +8413,18 @@ app.post(
8163
8413
  const endpointArgs = authReq.resolvedRestToolArgs ?? { ...args ?? {} };
8164
8414
  const keyedBeforeExecution = async (endpoint) => {
8165
8415
  const blocked = await debitApiKeyCreditForExecution(req, res);
8166
- if (blocked) return blocked;
8416
+ if (blocked) {
8417
+ if (blocked.code === "QUOTA_EXCEEDED") {
8418
+ recordPaywall(req, {
8419
+ code: "QUOTA_EXCEEDED",
8420
+ authLane: "api_key",
8421
+ transport: "rest",
8422
+ principalRaw: authReq.userId,
8423
+ toolName: endpoint.tool.name
8424
+ });
8425
+ }
8426
+ return blocked;
8427
+ }
8167
8428
  authReq.idempotencyExecutionStarted = true;
8168
8429
  if (authReq.hiveSubjectContext) {
8169
8430
  const materialized = await materializeSubjectContext(
@@ -8190,7 +8451,15 @@ app.post(
8190
8451
  requestId: req.requestId
8191
8452
  });
8192
8453
  if (restAuditEvent) recordSubjectAuditEvent(restAuditEvent);
8193
- return void 0;
8454
+ const remainingHeader = res.getHeader("X-Quota-Remaining");
8455
+ const creditCost = getMcpCreditCost(endpoint.tool.name);
8456
+ return {
8457
+ type: "billing",
8458
+ credits_used: creditCost,
8459
+ ...creditCost > 0 && remainingHeader !== void 0 && {
8460
+ credits_remaining: String(remainingHeader) === "unlimited" ? null : Number(remainingHeader)
8461
+ }
8462
+ };
8194
8463
  };
8195
8464
  const heroEndpoint = HERO_TOOL_NAMES.has(executionToolName) ? getRestHeroEndpoint(executionToolName) : void 0;
8196
8465
  const result = heroEndpoint ? await (async () => {
@@ -8198,6 +8467,7 @@ app.post(
8198
8467
  transport: "rest",
8199
8468
  requestId: req.requestId,
8200
8469
  clients: getSharedClients(),
8470
+ creditCost: getMcpCreditCost(executionToolName),
8201
8471
  analytics: {
8202
8472
  principalRaw: authReq.userId,
8203
8473
  authLane: "api_key",
@@ -8215,17 +8485,21 @@ app.post(
8215
8485
  };
8216
8486
  })() : await runWithSubjectContext(
8217
8487
  authReq.hiveSubjectContext,
8218
- () => executeToolByName(executionToolName, endpointArgs, {
8219
- requestId: req.requestId,
8220
- // Same identity context the hero branch passes — without it every
8221
- // non-hero keyed REST call wrote a NULL-principal analytics row.
8222
- analytics: {
8223
- principalRaw: authReq.userId,
8224
- authLane: "api_key",
8225
- isInternal: isMonitorSecretRequest(req)
8226
- },
8227
- beforeExecution: keyedBeforeExecution
8228
- })
8488
+ () => runWithRequestPrincipal(
8489
+ authReq.userId ? { key: `user:${authReq.userId}`, lane: "api_key", client: null } : void 0,
8490
+ () => executeToolByName(executionToolName, endpointArgs, {
8491
+ requestId: req.requestId,
8492
+ creditCost: getMcpCreditCost(executionToolName),
8493
+ // Same identity context the hero branch passes — without it every
8494
+ // non-hero keyed REST call wrote a NULL-principal analytics row.
8495
+ analytics: {
8496
+ principalRaw: authReq.userId,
8497
+ authLane: "api_key",
8498
+ isInternal: isMonitorSecretRequest(req)
8499
+ },
8500
+ beforeExecution: keyedBeforeExecution
8501
+ })
8502
+ )
8229
8503
  );
8230
8504
  const meta = buildRestExecutionMeta(
8231
8505
  executionToolName,
@@ -8239,7 +8513,8 @@ app.post(
8239
8513
  const status = REST_EXECUTE_STATUS_CODES[error.code] ?? 500;
8240
8514
  return res.status(status).json({
8241
8515
  ...apiError(error.type, error.code, error.message, {
8242
- requestId: req.requestId
8516
+ requestId: req.requestId,
8517
+ ...restErrorOptionsFromReceipt(result.meta)
8243
8518
  }),
8244
8519
  meta,
8245
8520
  ...result.validationErrors && {
@@ -8535,6 +8810,7 @@ if (isMainModule()) {
8535
8810
  }
8536
8811
  var server_default = app;
8537
8812
  export {
8813
+ anonymousPaywallToolName,
8538
8814
  buildMcpQuotaExhaustedCopy,
8539
8815
  classifyAnonymousMcpRequest,
8540
8816
  createHttpApp,
package/build/stdio.js CHANGED
@@ -6,8 +6,8 @@ import {
6
6
  import {
7
7
  createHiveMcpServer,
8
8
  secureLogger
9
- } from "./chunk-J2PXISXW.js";
10
- import "./chunk-GADPH72D.js";
9
+ } from "./chunk-LP7PDQCO.js";
10
+ import "./chunk-4QDGA6GP.js";
11
11
  import "./chunk-EPF36Q3Z.js";
12
12
  import {
13
13
  SERVER_NAME,