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.
- package/README.md +10 -4
- package/agent-skills/.claude-plugin/plugin.json +10 -0
- package/agent-skills/README.md +2 -0
- package/agent-skills/SKILL.md +18 -6
- package/agent-skills/evals/trigger-queries.json +70 -0
- package/agent-skills/package.json +3 -1
- package/agent-skills/packs/hive/SKILL.md +404 -0
- package/agent-skills/packs/hive/evals/evals.json +50 -0
- package/agent-skills/packs/hive/evals/triggers.json +39 -0
- package/agent-skills/packs/hive-build-onboarding/SKILL.md +1 -1
- package/agent-skills/packs/hive-cli/SKILL.md +2 -2
- package/agent-skills/packs/hive-mcp/SKILL.md +9 -5
- package/agent-skills/packs/hive-prediction-markets/SKILL.md +145 -0
- package/agent-skills/packs/hive-prediction-markets/evals/evals.json +29 -0
- package/agent-skills/packs/hive-prediction-markets/evals/triggers.json +14 -0
- package/agent-skills/packs/hive-query/SKILL.md +4 -2
- package/agent-skills/packs/hive-stateful-monitoring/SKILL.md +3 -3
- package/agent-skills/references/agents-routing.md +4 -1
- package/agent-skills/scripts/validate-agent-skills-package.mjs +2 -0
- package/agent-skills/skills.sh.json +9 -1
- package/build/{api-client-6AM3HVHQ.js → api-client-POBN6Y5S.js} +12 -3
- package/build/{auth-4LPSQJXD.js → auth-6XVL5CE3.js} +1 -1
- package/build/call-args-BUB6YNHW.js +27 -0
- package/build/{chunk-GADPH72D.js → chunk-4QDGA6GP.js} +52 -1
- package/build/{chunk-2SPCKQBJ.js → chunk-5QAXJON7.js} +71 -0
- package/build/chunk-APBLB62S.js +474 -0
- package/build/chunk-AZFOTKS2.js +394 -0
- package/build/{chunk-GYVF75XB.js → chunk-FQNW6SZ6.js} +1 -1
- package/build/{chunk-32NRUWXA.js → chunk-KQVPKTEO.js} +3 -3
- package/build/{chunk-LW2DHE32.js → chunk-L4KVJDA7.js} +1 -1
- package/build/{chunk-J2PXISXW.js → chunk-LP7PDQCO.js} +4289 -626
- package/build/{chunk-P7NLFSZQ.js → chunk-RNVZIPNY.js} +100 -9
- package/build/chunk-XN3T6YOG.js +32 -0
- package/build/{chunk-KUF754NT.js → chunk-YLHNUAFT.js} +52 -12
- package/build/{chunk-G23ZAW36.js → chunk-ZBMMJ6LE.js} +3 -2
- package/build/{chunk-LP5ECG7A.js → chunk-ZDXMZPC6.js} +26 -2
- package/build/{chunk-S4HBVZ7O.js → chunk-ZSPPJW4G.js} +44 -7
- package/build/cli.js +120 -48
- package/build/{completion-YVSWWF6D.js → completion-ROZAH667.js} +1 -1
- package/build/{doctor-AM6QST7Z.js → doctor-KPYW2U7D.js} +42 -5
- package/build/{errors-EL6UMXNJ.js → errors-YQAVCXXO.js} +9 -1
- package/build/feedback-G2DFCCI6.js +113 -0
- package/build/{init-all-54G3CVZA.js → init-all-JXLG7LOX.js} +5 -5
- package/build/{mcpServer-NWOKFR7Q.js → mcpServer-SUDGFNIF.js} +2 -2
- package/build/monitor-worker.js +62 -10
- package/build/{namespace-PA2IDNRT.js → namespace-XMFFW47T.js} +1 -1
- package/build/{output-XGTE4EWJ.js → output-BBHBJI32.js} +5 -1
- package/build/release.json +4 -4
- package/build/routing-4VKAVRYR.js +43 -0
- package/build/{serve-XRNMHIJX.js → serve-BYC3LRLN.js} +1 -1
- package/build/server.js +317 -41
- package/build/stdio.js +2 -2
- package/build/{tools-FNPMELSU.js → tools-NJQPIJ53.js} +81 -33
- package/build/{uninstall-P3JJZCXT.js → uninstall-4TJ4SZLU.js} +26 -7
- package/build/{upgrade-ATIFZANV.js → upgrade-3NR3LAGU.js} +2 -2
- package/build/{watch-FG2C5Y2Z.js → watch-BUSEPVDM.js} +5 -3
- 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-
|
|
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-
|
|
105
|
+
} from "./chunk-LP7PDQCO.js";
|
|
100
106
|
import {
|
|
101
107
|
HIVE_AGENT_SKILLS
|
|
102
|
-
} from "./chunk-
|
|
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
|
-
|
|
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:
|
|
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)
|
|
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
|
|
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)
|
|
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
|
|
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
|
-
() =>
|
|
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
|
-
|
|
8330
|
+
capCode,
|
|
8094
8331
|
globalCap ? ANON_GLOBAL_CAP_MESSAGE : ANON_QUOTA_EXHAUSTED_MESSAGE,
|
|
8095
|
-
{
|
|
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)
|
|
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
|
-
|
|
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
|
-
() =>
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
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