hive-intelligence 1.6.3 → 1.7.1
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 +382 -0
- package/agent-skills/packs/hive/evals/evals.json +50 -0
- package/agent-skills/packs/hive/evals/triggers.json +15 -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 +2 -1
- 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-UGNFUZ2L.js} +12 -3
- package/build/{auth-4LPSQJXD.js → auth-5TLYP2DC.js} +1 -1
- package/build/call-args-NVOYX6DL.js +27 -0
- package/build/{chunk-XZJICKAL.js → chunk-2C5FBG7B.js} +4 -3
- package/build/{chunk-IJ5KOZH5.js → chunk-3SQPLZW6.js} +42730 -39105
- package/build/{chunk-S4HBVZ7O.js → chunk-3Z5VR53Z.js} +36 -7
- package/build/{chunk-GADPH72D.js → chunk-4QDGA6GP.js} +52 -1
- package/build/{chunk-2SPCKQBJ.js → chunk-5QAXJON7.js} +71 -0
- package/build/{chunk-GYVF75XB.js → chunk-FQNW6SZ6.js} +1 -1
- package/build/{chunk-LW2DHE32.js → chunk-L4KVJDA7.js} +1 -1
- package/build/chunk-POPAEM4E.js +474 -0
- package/build/{chunk-P7NLFSZQ.js → chunk-TWYGJEVN.js} +100 -9
- package/build/{chunk-32NRUWXA.js → chunk-UPWYFTTJ.js} +3 -3
- package/build/{chunk-KUF754NT.js → chunk-WII7MTYZ.js} +52 -12
- package/build/chunk-XN3T6YOG.js +32 -0
- package/build/chunk-XT6RURQV.js +394 -0
- package/build/{chunk-LP5ECG7A.js → chunk-ZDXMZPC6.js} +26 -2
- package/build/cli.js +120 -48
- package/build/{completion-YVSWWF6D.js → completion-ROZAH667.js} +1 -1
- package/build/{doctor-YBCU5FLQ.js → doctor-44WSVNGJ.js} +42 -5
- package/build/{errors-EL6UMXNJ.js → errors-W3GXQN34.js} +7 -1
- package/build/feedback-FRRLZDAF.js +113 -0
- package/build/{init-all-54G3CVZA.js → init-all-LYRFE2YQ.js} +5 -5
- package/build/{mcpServer-V4XUSCX7.js → mcpServer-43LTLURB.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-CP43PP5F.js +43 -0
- package/build/{serve-SU67DIY3.js → serve-LTMDRKXS.js} +1 -1
- package/build/server.js +295 -41
- package/build/stdio.js +2 -2
- package/build/{tools-FNPMELSU.js → tools-VA7JXD3K.js} +81 -33
- package/build/{uninstall-P3JJZCXT.js → uninstall-3KJLHTNM.js} +26 -7
- package/build/{upgrade-ATIFZANV.js → upgrade-3NR3LAGU.js} +2 -2
- package/build/{watch-FG2C5Y2Z.js → watch-HJOTI32P.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-2C5FBG7B.js";
|
|
10
10
|
import {
|
|
11
11
|
CategoryEndpoints,
|
|
12
12
|
HERO_TOOL_NAMES,
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
createOAuthStore,
|
|
36
36
|
createToolNotFoundResult,
|
|
37
37
|
endpointAllowedForInvoker,
|
|
38
|
+
errorDocUrl,
|
|
38
39
|
executeEndpoint,
|
|
39
40
|
getAllWrappedEndpoints,
|
|
40
41
|
getAnalyticsWriter,
|
|
@@ -64,6 +65,7 @@ import {
|
|
|
64
65
|
heroTools,
|
|
65
66
|
isB2BSubjectsEnabled,
|
|
66
67
|
isEndpointInvokerName,
|
|
68
|
+
isFreeWriteEndpoint,
|
|
67
69
|
isHiveOAuthAccessToken,
|
|
68
70
|
isProduction,
|
|
69
71
|
isRedisAvailable,
|
|
@@ -74,6 +76,7 @@ import {
|
|
|
74
76
|
listKeys,
|
|
75
77
|
materializeSubjectContext,
|
|
76
78
|
normalizeInvokeArgs,
|
|
79
|
+
principalKeyFromRaw,
|
|
77
80
|
recordProviderCanary,
|
|
78
81
|
recordSubjectAuditEvent,
|
|
79
82
|
resolveEndpoint,
|
|
@@ -87,19 +90,21 @@ import {
|
|
|
87
90
|
runProviderCanaryQuick,
|
|
88
91
|
runTaskCanariesQuick,
|
|
89
92
|
runTaskCanary,
|
|
93
|
+
runWithRequestPrincipal,
|
|
90
94
|
runWithSubjectContext,
|
|
91
95
|
runWorkflowRegressionCanaries,
|
|
92
96
|
secureLogger,
|
|
97
|
+
submitFeedback,
|
|
93
98
|
trackCliCommand,
|
|
94
99
|
trackOAuthFirstMaterialReceipt,
|
|
95
100
|
trackServerEvent,
|
|
96
101
|
validateEndpointArgs,
|
|
97
102
|
verifyApiKey,
|
|
98
103
|
waitForRedisAvailability
|
|
99
|
-
} from "./chunk-
|
|
104
|
+
} from "./chunk-3SQPLZW6.js";
|
|
100
105
|
import {
|
|
101
106
|
HIVE_AGENT_SKILLS
|
|
102
|
-
} from "./chunk-
|
|
107
|
+
} from "./chunk-4QDGA6GP.js";
|
|
103
108
|
import "./chunk-EPF36Q3Z.js";
|
|
104
109
|
import {
|
|
105
110
|
PUBLIC_MCP_REGISTRY_ID,
|
|
@@ -762,19 +767,6 @@ import rateLimit from "express-rate-limit";
|
|
|
762
767
|
import RedisStore from "rate-limit-redis";
|
|
763
768
|
|
|
764
769
|
// 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
770
|
function apiError(type, code, message, opts) {
|
|
779
771
|
return {
|
|
780
772
|
ok: false,
|
|
@@ -784,7 +776,11 @@ function apiError(type, code, message, opts) {
|
|
|
784
776
|
message,
|
|
785
777
|
...opts?.param && { param: opts.param },
|
|
786
778
|
...opts?.requestId && { request_id: opts.requestId },
|
|
787
|
-
|
|
779
|
+
...opts?.cause && { cause: opts.cause },
|
|
780
|
+
...opts?.next_action && { next_action: opts.next_action },
|
|
781
|
+
...opts?.retryable !== void 0 && { retryable: opts.retryable },
|
|
782
|
+
...opts?.retry_after !== void 0 && { retry_after: opts.retry_after },
|
|
783
|
+
doc_url: errorDocUrl(code)
|
|
788
784
|
}
|
|
789
785
|
};
|
|
790
786
|
}
|
|
@@ -7191,8 +7187,10 @@ function classifyAnonymousMcpRequest(body) {
|
|
|
7191
7187
|
return "auth_required";
|
|
7192
7188
|
}
|
|
7193
7189
|
if (exposedToolName.startsWith("hive_")) return "auth_required";
|
|
7190
|
+
if (isFreeWriteEndpoint(exposedToolName)) return "open";
|
|
7194
7191
|
if (isEndpointInvokerName(exposedToolName)) {
|
|
7195
7192
|
const target = getInvokeEndpointName(body?.params?.arguments);
|
|
7193
|
+
if (target && isFreeWriteEndpoint(target)) return "open";
|
|
7196
7194
|
if (target?.startsWith("hive_")) return "auth_required";
|
|
7197
7195
|
return "material";
|
|
7198
7196
|
}
|
|
@@ -7413,6 +7411,13 @@ function createStatelessMcpHandler(scope) {
|
|
|
7413
7411
|
} else if (decision.kind === "quota_exhausted") {
|
|
7414
7412
|
res.setHeader("Retry-After", String(decision.resetSeconds));
|
|
7415
7413
|
const exhaustedMessage = decision.scope === "global" ? ANON_GLOBAL_CAP_MESSAGE : ANON_QUOTA_EXHAUSTED_MESSAGE;
|
|
7414
|
+
const exhaustedCode = decision.scope === "global" ? "ANON_GLOBAL_CAP_EXCEEDED" : "ANON_QUOTA_EXCEEDED";
|
|
7415
|
+
recordPaywall(req, {
|
|
7416
|
+
code: exhaustedCode,
|
|
7417
|
+
authLane: "anonymous",
|
|
7418
|
+
transport: "mcp",
|
|
7419
|
+
toolName: req.body?.params?.name
|
|
7420
|
+
});
|
|
7416
7421
|
if (mcpOAuth.status === "ready") {
|
|
7417
7422
|
const requiredScopes = getRequiredMcpScopes(req.body);
|
|
7418
7423
|
res.setHeader(
|
|
@@ -7434,9 +7439,12 @@ function createStatelessMcpHandler(scope) {
|
|
|
7434
7439
|
code: -32001,
|
|
7435
7440
|
message: exhaustedMessage,
|
|
7436
7441
|
data: {
|
|
7437
|
-
code:
|
|
7442
|
+
code: exhaustedCode,
|
|
7438
7443
|
retryable: true,
|
|
7439
|
-
retry_after: decision.resetSeconds
|
|
7444
|
+
retry_after: decision.resetSeconds,
|
|
7445
|
+
cause: decision.scope === "global" ? "Hive's shared keyless allowance for today is spent." : "This IP's 25 free material calls for today are spent.",
|
|
7446
|
+
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.",
|
|
7447
|
+
doc_url: errorDocUrl(exhaustedCode)
|
|
7440
7448
|
}
|
|
7441
7449
|
},
|
|
7442
7450
|
id: req.body?.id ?? null
|
|
@@ -7668,6 +7676,7 @@ function createStatelessMcpHandler(scope) {
|
|
|
7668
7676
|
} : void 0,
|
|
7669
7677
|
beforeToolExecution: keyedAuth?.userId ? async (executedToolName) => {
|
|
7670
7678
|
const creditCost = getMcpCreditCost(executedToolName);
|
|
7679
|
+
let keyedBalance;
|
|
7671
7680
|
res.setHeader("X-Hive-Credit-Cost", String(creditCost));
|
|
7672
7681
|
if (creditCost > 0) {
|
|
7673
7682
|
const debit = await debitMcpCredits({
|
|
@@ -7687,7 +7696,16 @@ function createStatelessMcpHandler(scope) {
|
|
|
7687
7696
|
next_action: "Visit https://hiveintelligence.xyz/dashboard to initialize billing, or contact support. Retrying will not help until the wallet exists."
|
|
7688
7697
|
};
|
|
7689
7698
|
}
|
|
7690
|
-
if (debit.rateLimited)
|
|
7699
|
+
if (debit.rateLimited) {
|
|
7700
|
+
res.setHeader("Retry-After", "60");
|
|
7701
|
+
recordPaywall(req, {
|
|
7702
|
+
code: "QUOTA_EXCEEDED",
|
|
7703
|
+
authLane: keyedAuth.authMethod,
|
|
7704
|
+
transport: "mcp",
|
|
7705
|
+
principalRaw: keyedAuth.userId,
|
|
7706
|
+
toolName: executedToolName
|
|
7707
|
+
});
|
|
7708
|
+
}
|
|
7691
7709
|
return {
|
|
7692
7710
|
type: debit.rateLimited ? "rate_limit_error" : "internal_error",
|
|
7693
7711
|
code: debit.rateLimited ? "QUOTA_EXCEEDED" : "QUOTA_SERVICE_UNAVAILABLE",
|
|
@@ -7701,6 +7719,7 @@ function createStatelessMcpHandler(scope) {
|
|
|
7701
7719
|
"X-Quota-Remaining",
|
|
7702
7720
|
debit.balance === null ? "unlimited" : String(debit.balance)
|
|
7703
7721
|
);
|
|
7722
|
+
keyedBalance = debit.balance;
|
|
7704
7723
|
}
|
|
7705
7724
|
if (subjectContext) {
|
|
7706
7725
|
const materialized = await materializeSubjectContext(subjectContext);
|
|
@@ -7725,12 +7744,24 @@ function createStatelessMcpHandler(scope) {
|
|
|
7725
7744
|
requestId: req.requestId
|
|
7726
7745
|
});
|
|
7727
7746
|
if (auditEvent) recordSubjectAuditEvent(auditEvent);
|
|
7728
|
-
return
|
|
7747
|
+
return {
|
|
7748
|
+
type: "billing",
|
|
7749
|
+
credits_used: creditCost,
|
|
7750
|
+
...creditCost > 0 && { credits_remaining: keyedBalance }
|
|
7751
|
+
};
|
|
7729
7752
|
} : anonymousLane ? async (executedToolName) => {
|
|
7730
7753
|
const creditCost = getMcpCreditCost(executedToolName);
|
|
7731
7754
|
res.setHeader("X-Hive-Credit-Cost", String(creditCost));
|
|
7732
|
-
if (creditCost === 0)
|
|
7755
|
+
if (creditCost === 0) {
|
|
7756
|
+
return { type: "billing", credits_used: 0 };
|
|
7757
|
+
}
|
|
7733
7758
|
if (!anonymousLane.materialGranted || executedToolName.startsWith("hive_")) {
|
|
7759
|
+
recordPaywall(req, {
|
|
7760
|
+
code: "ANON_AUTH_REQUIRED",
|
|
7761
|
+
authLane: "anonymous",
|
|
7762
|
+
transport: "mcp",
|
|
7763
|
+
toolName: executedToolName
|
|
7764
|
+
});
|
|
7734
7765
|
return {
|
|
7735
7766
|
type: "authentication_error",
|
|
7736
7767
|
code: "ANON_AUTH_REQUIRED",
|
|
@@ -7738,7 +7769,11 @@ function createStatelessMcpHandler(scope) {
|
|
|
7738
7769
|
runtimeStatus: "failing"
|
|
7739
7770
|
};
|
|
7740
7771
|
}
|
|
7741
|
-
return
|
|
7772
|
+
return {
|
|
7773
|
+
type: "billing",
|
|
7774
|
+
credits_used: creditCost,
|
|
7775
|
+
credits_remaining: anonymousLane.quotaRemaining ?? null
|
|
7776
|
+
};
|
|
7742
7777
|
} : void 0
|
|
7743
7778
|
}).getServer();
|
|
7744
7779
|
const transport = new StreamableHTTPServerTransport({
|
|
@@ -7752,7 +7787,17 @@ function createStatelessMcpHandler(scope) {
|
|
|
7752
7787
|
await server.connect(transport);
|
|
7753
7788
|
await runWithSubjectContext(
|
|
7754
7789
|
subjectContext,
|
|
7755
|
-
() =>
|
|
7790
|
+
() => runWithRequestPrincipal(
|
|
7791
|
+
analytics.principalRaw && analytics.authLane ? {
|
|
7792
|
+
key: principalKeyFromRaw(
|
|
7793
|
+
analytics.principalRaw,
|
|
7794
|
+
analytics.authLane
|
|
7795
|
+
),
|
|
7796
|
+
lane: analytics.authLane,
|
|
7797
|
+
client: analytics.clientName ?? null
|
|
7798
|
+
} : void 0,
|
|
7799
|
+
() => transport.handleRequest(req, res, body)
|
|
7800
|
+
)
|
|
7756
7801
|
);
|
|
7757
7802
|
secureLogger.debug("MCP request completed", {
|
|
7758
7803
|
sessionId: sessionCorrelationId,
|
|
@@ -7968,6 +8013,161 @@ var REST_EXECUTE_STATUS_CODES = {
|
|
|
7968
8013
|
STATE_SUBJECT_ARCHIVED: 410,
|
|
7969
8014
|
STATE_BACKEND_UNAVAILABLE: 503
|
|
7970
8015
|
};
|
|
8016
|
+
function restErrorOptionsFromReceipt(meta) {
|
|
8017
|
+
return {
|
|
8018
|
+
...meta.cause && { cause: meta.cause },
|
|
8019
|
+
...meta.next_action && { next_action: meta.next_action },
|
|
8020
|
+
retryable: meta.runtime_status === "rate_limited" || meta.runtime_status === "degraded" || meta.runtime_status === "failing"
|
|
8021
|
+
};
|
|
8022
|
+
}
|
|
8023
|
+
function recordPaywall(req, event) {
|
|
8024
|
+
try {
|
|
8025
|
+
getAnalyticsWriter().enqueuePaywall({
|
|
8026
|
+
code: event.code,
|
|
8027
|
+
authLane: event.authLane,
|
|
8028
|
+
transport: event.transport,
|
|
8029
|
+
principalRaw: event.principalRaw ?? `anon:${anonymousIpHash(ipRateLimitKeyFromRequest(req))}`,
|
|
8030
|
+
toolName: typeof event.toolName === "string" ? event.toolName : null,
|
|
8031
|
+
isInternal: isMonitorSecretRequest(req)
|
|
8032
|
+
});
|
|
8033
|
+
} catch {
|
|
8034
|
+
}
|
|
8035
|
+
}
|
|
8036
|
+
async function resolveOptionalRestPrincipal(req) {
|
|
8037
|
+
if (mcpCredentialPresented(req)) {
|
|
8038
|
+
const auth = await validateMcpCredential(req);
|
|
8039
|
+
if (!auth.ok) {
|
|
8040
|
+
return {
|
|
8041
|
+
ok: false,
|
|
8042
|
+
status: 401,
|
|
8043
|
+
code: "UNAUTHORIZED",
|
|
8044
|
+
message: "The credential presented was rejected."
|
|
8045
|
+
};
|
|
8046
|
+
}
|
|
8047
|
+
const principalId = auth.userId ?? auth.keyId ?? "static";
|
|
8048
|
+
return {
|
|
8049
|
+
ok: true,
|
|
8050
|
+
key: `user:${principalId}`,
|
|
8051
|
+
lane: auth.authMethod,
|
|
8052
|
+
userId: auth.userId
|
|
8053
|
+
};
|
|
8054
|
+
}
|
|
8055
|
+
return {
|
|
8056
|
+
ok: true,
|
|
8057
|
+
key: `anon:${anonymousIpHash(ipRateLimitKeyFromRequest(req))}`,
|
|
8058
|
+
lane: "anonymous"
|
|
8059
|
+
};
|
|
8060
|
+
}
|
|
8061
|
+
app.post("/api/v1/feedback", async (req, res) => {
|
|
8062
|
+
const principal = await resolveOptionalRestPrincipal(req);
|
|
8063
|
+
if (!principal.ok) {
|
|
8064
|
+
res.status(principal.status).json(
|
|
8065
|
+
apiError("authentication_error", principal.code, principal.message, {
|
|
8066
|
+
requestId: req.requestId
|
|
8067
|
+
})
|
|
8068
|
+
);
|
|
8069
|
+
return;
|
|
8070
|
+
}
|
|
8071
|
+
const body = req.body ?? {};
|
|
8072
|
+
const result = await submitFeedback({
|
|
8073
|
+
principalKey: principal.key,
|
|
8074
|
+
lane: principal.lane,
|
|
8075
|
+
client: typeof body.client === "string" ? body.client : null,
|
|
8076
|
+
message: body.message,
|
|
8077
|
+
tool: body.tool,
|
|
8078
|
+
receiptId: body.receipt_id
|
|
8079
|
+
});
|
|
8080
|
+
if (result.ok) {
|
|
8081
|
+
res.json({
|
|
8082
|
+
ok: true,
|
|
8083
|
+
data: { id: result.id, remaining_today: result.remaining, reset_at: result.resetAt }
|
|
8084
|
+
});
|
|
8085
|
+
return;
|
|
8086
|
+
}
|
|
8087
|
+
const status = result.code === "VALIDATION_ERROR" ? 400 : result.code === "FEEDBACK_RATE_LIMITED" ? 429 : 503;
|
|
8088
|
+
if (result.resetAt) {
|
|
8089
|
+
const seconds = Math.max(
|
|
8090
|
+
1,
|
|
8091
|
+
Math.round((Date.parse(result.resetAt) - Date.now()) / 1e3)
|
|
8092
|
+
);
|
|
8093
|
+
res.setHeader("Retry-After", String(seconds));
|
|
8094
|
+
}
|
|
8095
|
+
res.status(status).json({
|
|
8096
|
+
...apiError(
|
|
8097
|
+
result.code === "VALIDATION_ERROR" ? "invalid_request_error" : result.code === "FEEDBACK_RATE_LIMITED" ? "rate_limit_error" : "internal_error",
|
|
8098
|
+
result.code,
|
|
8099
|
+
result.message,
|
|
8100
|
+
{
|
|
8101
|
+
requestId: req.requestId,
|
|
8102
|
+
retryable: result.retryable,
|
|
8103
|
+
...result.resetAt && {
|
|
8104
|
+
next_action: `Retry after ${result.resetAt}.`
|
|
8105
|
+
}
|
|
8106
|
+
}
|
|
8107
|
+
),
|
|
8108
|
+
...result.resetAt && { reset_at: result.resetAt }
|
|
8109
|
+
});
|
|
8110
|
+
});
|
|
8111
|
+
app.get("/api/v1/quota", async (req, res) => {
|
|
8112
|
+
const principal = await resolveOptionalRestPrincipal(req);
|
|
8113
|
+
if (!principal.ok) {
|
|
8114
|
+
res.status(principal.status).json(
|
|
8115
|
+
apiError("authentication_error", principal.code, principal.message, {
|
|
8116
|
+
requestId: req.requestId
|
|
8117
|
+
})
|
|
8118
|
+
);
|
|
8119
|
+
return;
|
|
8120
|
+
}
|
|
8121
|
+
try {
|
|
8122
|
+
if (principal.lane === "anonymous") {
|
|
8123
|
+
if (!isSupabaseConfigured()) {
|
|
8124
|
+
res.json({
|
|
8125
|
+
ok: true,
|
|
8126
|
+
data: { lane: "anonymous", remaining: null, limit: getAnonDailyMaterialCap(), reset_at: null }
|
|
8127
|
+
});
|
|
8128
|
+
return;
|
|
8129
|
+
}
|
|
8130
|
+
const { data, error } = await getSupabaseAdmin().rpc(
|
|
8131
|
+
"read_anon_daily_quota",
|
|
8132
|
+
{
|
|
8133
|
+
p_ip_hash: anonymousIpHash(ipRateLimitKeyFromRequest(req)),
|
|
8134
|
+
p_ip_limit: getAnonDailyMaterialCap()
|
|
8135
|
+
}
|
|
8136
|
+
);
|
|
8137
|
+
if (error) throw new Error(error.message);
|
|
8138
|
+
const row = Array.isArray(data) ? data[0] : data;
|
|
8139
|
+
const resetSeconds = Math.max(1, row?.reset_seconds ?? 1);
|
|
8140
|
+
res.json({
|
|
8141
|
+
ok: true,
|
|
8142
|
+
data: {
|
|
8143
|
+
lane: "anonymous",
|
|
8144
|
+
remaining: row?.remaining ?? null,
|
|
8145
|
+
limit: row?.limit_value ?? getAnonDailyMaterialCap(),
|
|
8146
|
+
reset_at: new Date(Date.now() + resetSeconds * 1e3).toISOString()
|
|
8147
|
+
}
|
|
8148
|
+
});
|
|
8149
|
+
return;
|
|
8150
|
+
}
|
|
8151
|
+
const wallet = principal.userId ? await getWalletStatus(principal.userId) : null;
|
|
8152
|
+
res.json({
|
|
8153
|
+
ok: true,
|
|
8154
|
+
data: {
|
|
8155
|
+
lane: principal.lane,
|
|
8156
|
+
remaining: wallet ? wallet.balance : null,
|
|
8157
|
+
limit: wallet ? wallet.monthlyGrant : null,
|
|
8158
|
+
reset_at: wallet ? wallet.cycleEnd : null
|
|
8159
|
+
}
|
|
8160
|
+
});
|
|
8161
|
+
} catch (error) {
|
|
8162
|
+
secureLogger.error("Quota read failed", error, { requestId: req.requestId });
|
|
8163
|
+
res.status(503).json(
|
|
8164
|
+
apiError("internal_error", "QUOTA_SERVICE_UNAVAILABLE", "The quota read is unavailable right now.", {
|
|
8165
|
+
requestId: req.requestId,
|
|
8166
|
+
retryable: true
|
|
8167
|
+
})
|
|
8168
|
+
);
|
|
8169
|
+
}
|
|
8170
|
+
});
|
|
7971
8171
|
function buildRestExecutionMeta(toolName, durationMs, meta) {
|
|
7972
8172
|
return {
|
|
7973
8173
|
tool: toolName,
|
|
@@ -7990,6 +8190,14 @@ function buildRestExecutionMeta(toolName, durationMs, meta) {
|
|
|
7990
8190
|
origin_source: meta.origin_source,
|
|
7991
8191
|
truncated: meta.truncated,
|
|
7992
8192
|
warnings: meta.warnings,
|
|
8193
|
+
...meta.credit_cost !== void 0 && { credit_cost: meta.credit_cost },
|
|
8194
|
+
...meta.credits_used !== void 0 && { credits_used: meta.credits_used },
|
|
8195
|
+
...meta.credits_remaining !== void 0 && {
|
|
8196
|
+
credits_remaining: meta.credits_remaining
|
|
8197
|
+
},
|
|
8198
|
+
...meta.cause && { cause: meta.cause },
|
|
8199
|
+
...meta.next_action && { next_action: meta.next_action },
|
|
8200
|
+
...meta.doc_url && { doc_url: meta.doc_url },
|
|
7993
8201
|
// Agent-actionable copy travels on every transport, not only MCP: REST and
|
|
7994
8202
|
// CLI callers were getting "not found" with no replacement (2026-09-14).
|
|
7995
8203
|
...meta.cause !== void 0 && { cause: meta.cause },
|
|
@@ -8086,13 +8294,27 @@ var anonymousHeroExecuteMiddleware = asyncHandler(
|
|
|
8086
8294
|
return;
|
|
8087
8295
|
}
|
|
8088
8296
|
const globalCap = quota.reason === "global_cap";
|
|
8297
|
+
const capCode = globalCap ? "ANON_GLOBAL_CAP_EXCEEDED" : "ANON_QUOTA_EXCEEDED";
|
|
8298
|
+
recordPaywall(req, {
|
|
8299
|
+
code: capCode,
|
|
8300
|
+
authLane: "anonymous",
|
|
8301
|
+
transport: "rest",
|
|
8302
|
+
principalRaw: `anon:${ipHash}`,
|
|
8303
|
+
toolName
|
|
8304
|
+
});
|
|
8089
8305
|
res.setHeader("Retry-After", String(quota.resetSeconds));
|
|
8090
8306
|
res.status(429).json(
|
|
8091
8307
|
apiError(
|
|
8092
8308
|
"rate_limit_error",
|
|
8093
|
-
|
|
8309
|
+
capCode,
|
|
8094
8310
|
globalCap ? ANON_GLOBAL_CAP_MESSAGE : ANON_QUOTA_EXHAUSTED_MESSAGE,
|
|
8095
|
-
{
|
|
8311
|
+
{
|
|
8312
|
+
requestId: req.requestId,
|
|
8313
|
+
cause: globalCap ? "Hive's shared keyless allowance for today is spent." : "This IP's free material calls for today are spent.",
|
|
8314
|
+
next_action: "Set HIVE_API_KEY (free at https://hiveintelligence.xyz/dashboard/keys); keys are not subject to the keyless cap.",
|
|
8315
|
+
retryable: true,
|
|
8316
|
+
retry_after: quota.resetSeconds
|
|
8317
|
+
}
|
|
8096
8318
|
)
|
|
8097
8319
|
);
|
|
8098
8320
|
return;
|
|
@@ -8102,6 +8324,12 @@ var anonymousHeroExecuteMiddleware = asyncHandler(
|
|
|
8102
8324
|
transport: "rest",
|
|
8103
8325
|
requestId: req.requestId,
|
|
8104
8326
|
clients: getSharedClients(),
|
|
8327
|
+
creditCost: getMcpCreditCost(toolName),
|
|
8328
|
+
beforeExecution: async () => ({
|
|
8329
|
+
type: "billing",
|
|
8330
|
+
credits_used: getMcpCreditCost(toolName),
|
|
8331
|
+
credits_remaining: quota.remaining
|
|
8332
|
+
}),
|
|
8105
8333
|
analytics: {
|
|
8106
8334
|
principalRaw: `anon:${ipHash}`,
|
|
8107
8335
|
authLane: "anonymous",
|
|
@@ -8125,7 +8353,8 @@ var anonymousHeroExecuteMiddleware = asyncHandler(
|
|
|
8125
8353
|
};
|
|
8126
8354
|
res.status(REST_EXECUTE_STATUS_CODES[error.code] ?? 500).json({
|
|
8127
8355
|
...apiError(error.type, error.code, error.message, {
|
|
8128
|
-
requestId: req.requestId
|
|
8356
|
+
requestId: req.requestId,
|
|
8357
|
+
...restErrorOptionsFromReceipt(result.hive)
|
|
8129
8358
|
}),
|
|
8130
8359
|
meta,
|
|
8131
8360
|
...result.validationErrors && {
|
|
@@ -8163,7 +8392,18 @@ app.post(
|
|
|
8163
8392
|
const endpointArgs = authReq.resolvedRestToolArgs ?? { ...args ?? {} };
|
|
8164
8393
|
const keyedBeforeExecution = async (endpoint) => {
|
|
8165
8394
|
const blocked = await debitApiKeyCreditForExecution(req, res);
|
|
8166
|
-
if (blocked)
|
|
8395
|
+
if (blocked) {
|
|
8396
|
+
if (blocked.code === "QUOTA_EXCEEDED") {
|
|
8397
|
+
recordPaywall(req, {
|
|
8398
|
+
code: "QUOTA_EXCEEDED",
|
|
8399
|
+
authLane: "api_key",
|
|
8400
|
+
transport: "rest",
|
|
8401
|
+
principalRaw: authReq.userId,
|
|
8402
|
+
toolName: endpoint.tool.name
|
|
8403
|
+
});
|
|
8404
|
+
}
|
|
8405
|
+
return blocked;
|
|
8406
|
+
}
|
|
8167
8407
|
authReq.idempotencyExecutionStarted = true;
|
|
8168
8408
|
if (authReq.hiveSubjectContext) {
|
|
8169
8409
|
const materialized = await materializeSubjectContext(
|
|
@@ -8190,7 +8430,15 @@ app.post(
|
|
|
8190
8430
|
requestId: req.requestId
|
|
8191
8431
|
});
|
|
8192
8432
|
if (restAuditEvent) recordSubjectAuditEvent(restAuditEvent);
|
|
8193
|
-
|
|
8433
|
+
const remainingHeader = res.getHeader("X-Quota-Remaining");
|
|
8434
|
+
const creditCost = getMcpCreditCost(endpoint.tool.name);
|
|
8435
|
+
return {
|
|
8436
|
+
type: "billing",
|
|
8437
|
+
credits_used: creditCost,
|
|
8438
|
+
...creditCost > 0 && remainingHeader !== void 0 && {
|
|
8439
|
+
credits_remaining: String(remainingHeader) === "unlimited" ? null : Number(remainingHeader)
|
|
8440
|
+
}
|
|
8441
|
+
};
|
|
8194
8442
|
};
|
|
8195
8443
|
const heroEndpoint = HERO_TOOL_NAMES.has(executionToolName) ? getRestHeroEndpoint(executionToolName) : void 0;
|
|
8196
8444
|
const result = heroEndpoint ? await (async () => {
|
|
@@ -8198,6 +8446,7 @@ app.post(
|
|
|
8198
8446
|
transport: "rest",
|
|
8199
8447
|
requestId: req.requestId,
|
|
8200
8448
|
clients: getSharedClients(),
|
|
8449
|
+
creditCost: getMcpCreditCost(executionToolName),
|
|
8201
8450
|
analytics: {
|
|
8202
8451
|
principalRaw: authReq.userId,
|
|
8203
8452
|
authLane: "api_key",
|
|
@@ -8215,17 +8464,21 @@ app.post(
|
|
|
8215
8464
|
};
|
|
8216
8465
|
})() : await runWithSubjectContext(
|
|
8217
8466
|
authReq.hiveSubjectContext,
|
|
8218
|
-
() =>
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8467
|
+
() => runWithRequestPrincipal(
|
|
8468
|
+
authReq.userId ? { key: `user:${authReq.userId}`, lane: "api_key", client: null } : void 0,
|
|
8469
|
+
() => executeToolByName(executionToolName, endpointArgs, {
|
|
8470
|
+
requestId: req.requestId,
|
|
8471
|
+
creditCost: getMcpCreditCost(executionToolName),
|
|
8472
|
+
// Same identity context the hero branch passes — without it every
|
|
8473
|
+
// non-hero keyed REST call wrote a NULL-principal analytics row.
|
|
8474
|
+
analytics: {
|
|
8475
|
+
principalRaw: authReq.userId,
|
|
8476
|
+
authLane: "api_key",
|
|
8477
|
+
isInternal: isMonitorSecretRequest(req)
|
|
8478
|
+
},
|
|
8479
|
+
beforeExecution: keyedBeforeExecution
|
|
8480
|
+
})
|
|
8481
|
+
)
|
|
8229
8482
|
);
|
|
8230
8483
|
const meta = buildRestExecutionMeta(
|
|
8231
8484
|
executionToolName,
|
|
@@ -8239,7 +8492,8 @@ app.post(
|
|
|
8239
8492
|
const status = REST_EXECUTE_STATUS_CODES[error.code] ?? 500;
|
|
8240
8493
|
return res.status(status).json({
|
|
8241
8494
|
...apiError(error.type, error.code, error.message, {
|
|
8242
|
-
requestId: req.requestId
|
|
8495
|
+
requestId: req.requestId,
|
|
8496
|
+
...restErrorOptionsFromReceipt(result.meta)
|
|
8243
8497
|
}),
|
|
8244
8498
|
meta,
|
|
8245
8499
|
...result.validationErrors && {
|
package/build/stdio.js
CHANGED