hive-intelligence 1.1.3 → 1.1.4

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 (112) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +175 -54
  3. package/agent-skills/.claude-plugin/plugin.json +6 -0
  4. package/agent-skills/.mcp.json +11 -0
  5. package/agent-skills/AGENTS.md +37 -0
  6. package/agent-skills/README.md +13 -14
  7. package/agent-skills/SKILL.md +16 -4
  8. package/agent-skills/evals/trigger-queries.json +270 -0
  9. package/agent-skills/package.json +3 -3
  10. package/agent-skills/packs/hive-build/SKILL.md +11 -10
  11. package/agent-skills/packs/hive-build/evals/evals.json +2 -2
  12. package/agent-skills/packs/hive-mcp/SKILL.md +44 -121
  13. package/agent-skills/packs/hive-mcp/evals/evals.json +1 -1
  14. package/agent-skills/packs/hive-mcp/references/client-install-matrix.md +1 -1
  15. package/agent-skills/packs/hive-mcp/references/clients.md +141 -0
  16. package/agent-skills/packs/hive-query/SKILL.md +3 -2
  17. package/agent-skills/packs/hive-query/references/root-mcp-workflow.md +1 -1
  18. package/agent-skills/packs/hive-stateful-monitoring/SKILL.md +75 -0
  19. package/agent-skills/packs/hive-stateful-monitoring/evals/evals.json +49 -0
  20. package/agent-skills/packs/hive-tool-discovery/SKILL.md +1 -1
  21. package/agent-skills/references/agents-routing.md +2 -0
  22. package/agent-skills/scripts/validate-agent-skills-package.mjs +49 -11
  23. package/agent-skills/skills.sh.json +3 -2
  24. package/build/{alias-CZ3DEYHF.js → alias-3OPMQF4X.js} +2 -3
  25. package/build/{api-client-I23XLSHU.js → api-client-EPF6FD7A.js} +4 -4
  26. package/build/{auth-KKINNBXT.js → auth-Q5Q24IRI.js} +3 -4
  27. package/build/{browser-auth-EEFGRHWY.js → browser-auth-VPF5LZMZ.js} +2 -3
  28. package/build/{cache-EGNMENNA.js → cache-T6KC5YEY.js} +2 -3
  29. package/build/{chunk-KBVPIZS4.js → chunk-3WYWXTNN.js} +0 -1
  30. package/build/{chunk-2MYMHMBF.js → chunk-4F5CI6LX.js} +6 -6
  31. package/build/{chunk-RKICZ6DZ.js → chunk-5XXI25JE.js} +7 -8
  32. package/build/{chunk-IZTN7WFH.js → chunk-7JWXBKEJ.js} +5 -3
  33. package/build/chunk-CEWCB5YS.js +71 -0
  34. package/build/{chunk-MPIUUE3T.js → chunk-FH3YGSY5.js} +0 -1
  35. package/build/{chunk-QNCI4DEB.js → chunk-FRXMGRZQ.js} +35 -21
  36. package/build/{chunk-PGBVNMNZ.js → chunk-GZPDEXO4.js} +0 -1
  37. package/build/{chunk-UKHZIVDT.js → chunk-IZPAMRHP.js} +5 -6
  38. package/build/{chunk-2X3U4DFK.js → chunk-JTUKQWIO.js} +3267 -916
  39. package/build/{chunk-XYBEFFZ3.js → chunk-NP6NSOG2.js} +1 -2
  40. package/build/{chunk-V77LO4RO.js → chunk-POT2MBEK.js} +0 -1
  41. package/build/{chunk-B6G222LS.js → chunk-VBOKVQ74.js} +18 -3
  42. package/build/{chunk-DYJBTKNR.js → chunk-YZ2PXBJK.js} +5 -3
  43. package/build/chunk-ZCU7XQKF.js +14 -0
  44. package/build/{chunk-KGYBTXIN.js → chunk-ZTJVOWDQ.js} +0 -1
  45. package/build/{chunk-IDZD2NVG.js → chunk-ZXB53E7H.js} +5 -6
  46. package/build/cli.js +56 -54
  47. package/build/{completion-BELZL7ND.js → completion-46K674EL.js} +2 -3
  48. package/build/{config-TDX4VUGW.js → config-XE6Q5P4I.js} +3 -4
  49. package/build/{config-dir-V43W2WIA.js → config-dir-MAWX5AUF.js} +2 -3
  50. package/build/{doctor-5K5UWHNX.js → doctor-HGMQE6IN.js} +33 -9
  51. package/build/{errors-E6MVUBNA.js → errors-J2PH3NLJ.js} +2 -3
  52. package/build/{format-TSFSI2K7.js → format-5X2HIVDT.js} +1 -2
  53. package/build/{init-YQ5NBICF.js → init-JMDNM7ZZ.js} +1 -2
  54. package/build/{init-all-6OWEQALB.js → init-all-5SKNV75X.js} +10 -11
  55. package/build/{mcpServer-AEX3RQ4U.js → mcpServer-5TKKSKJM.js} +7 -6
  56. package/build/monitor-worker.js +1140 -0
  57. package/build/{namespace-PVHBFDW2.js → namespace-FSX2G3NM.js} +2 -3
  58. package/build/{open-YUIJP37F.js → open-5A6ZQMGC.js} +2 -3
  59. package/build/{output-IYJBFCYK.js → output-V3D4YZ6U.js} +2 -3
  60. package/build/{serve-KWNJK6EY.js → serve-AGCOXXFM.js} +9 -4
  61. package/build/server.js +437 -145
  62. package/build/{setup-YTUKMPNP.js → setup-RMETGJWK.js} +4 -5
  63. package/build/stdio.js +9 -8
  64. package/build/{telemetry-5IPRKZBL.js → telemetry-WCME5XEU.js} +4 -4
  65. package/build/{tools-PJHWFCAG.js → tools-KAB6QBZC.js} +15 -15
  66. package/build/{uninstall-HWXEIP6K.js → uninstall-FLJMBBSC.js} +7 -8
  67. package/build/update-check-LNZQK3XA.js +18 -0
  68. package/build/upgrade-DTUIHVPS.js +171 -0
  69. package/build/version-I6HDAF4L.js +14 -0
  70. package/build/{watch-KZ5EDZK3.js → watch-YNN255QX.js} +7 -7
  71. package/package.json +14 -9
  72. package/build/alias-CZ3DEYHF.js.map +0 -1
  73. package/build/api-client-I23XLSHU.js.map +0 -1
  74. package/build/auth-KKINNBXT.js.map +0 -1
  75. package/build/browser-auth-EEFGRHWY.js.map +0 -1
  76. package/build/cache-EGNMENNA.js.map +0 -1
  77. package/build/chunk-2MYMHMBF.js.map +0 -1
  78. package/build/chunk-2X3U4DFK.js.map +0 -1
  79. package/build/chunk-B6G222LS.js.map +0 -1
  80. package/build/chunk-DYJBTKNR.js.map +0 -1
  81. package/build/chunk-IDZD2NVG.js.map +0 -1
  82. package/build/chunk-IZTN7WFH.js.map +0 -1
  83. package/build/chunk-KBVPIZS4.js.map +0 -1
  84. package/build/chunk-KGYBTXIN.js.map +0 -1
  85. package/build/chunk-MPIUUE3T.js.map +0 -1
  86. package/build/chunk-PGBVNMNZ.js.map +0 -1
  87. package/build/chunk-QNCI4DEB.js.map +0 -1
  88. package/build/chunk-RKICZ6DZ.js.map +0 -1
  89. package/build/chunk-UKHZIVDT.js.map +0 -1
  90. package/build/chunk-V77LO4RO.js.map +0 -1
  91. package/build/chunk-XYBEFFZ3.js.map +0 -1
  92. package/build/cli.js.map +0 -1
  93. package/build/completion-BELZL7ND.js.map +0 -1
  94. package/build/config-TDX4VUGW.js.map +0 -1
  95. package/build/config-dir-V43W2WIA.js.map +0 -1
  96. package/build/doctor-5K5UWHNX.js.map +0 -1
  97. package/build/errors-E6MVUBNA.js.map +0 -1
  98. package/build/format-TSFSI2K7.js.map +0 -1
  99. package/build/init-YQ5NBICF.js.map +0 -1
  100. package/build/init-all-6OWEQALB.js.map +0 -1
  101. package/build/mcpServer-AEX3RQ4U.js.map +0 -1
  102. package/build/namespace-PVHBFDW2.js.map +0 -1
  103. package/build/open-YUIJP37F.js.map +0 -1
  104. package/build/output-IYJBFCYK.js.map +0 -1
  105. package/build/serve-KWNJK6EY.js.map +0 -1
  106. package/build/server.js.map +0 -1
  107. package/build/setup-YTUKMPNP.js.map +0 -1
  108. package/build/stdio.js.map +0 -1
  109. package/build/telemetry-5IPRKZBL.js.map +0 -1
  110. package/build/tools-PJHWFCAG.js.map +0 -1
  111. package/build/uninstall-HWXEIP6K.js.map +0 -1
  112. package/build/watch-KZ5EDZK3.js.map +0 -1
@@ -0,0 +1,1140 @@
1
+ import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
2
+ import {
3
+ executeToolByName
4
+ } from "./chunk-CEWCB5YS.js";
5
+ import {
6
+ closeRedisClient,
7
+ getSupabaseAdmin,
8
+ isSupabaseConfigured,
9
+ secureLogger
10
+ } from "./chunk-JTUKQWIO.js";
11
+ import "./chunk-VBOKVQ74.js";
12
+ import "./chunk-ZCU7XQKF.js";
13
+ import "./chunk-3WYWXTNN.js";
14
+
15
+ // src/monitor-worker.ts
16
+ import { resolve } from "path";
17
+ import { fileURLToPath } from "url";
18
+
19
+ // src/services/monitor-runner.ts
20
+ import { createHash } from "crypto";
21
+ var DEFAULT_BATCH_SIZE = 10;
22
+ var DEFAULT_LOCK_TIMEOUT_SECONDS = 300;
23
+ var DEFAULT_WORKER_ID = `hive-monitor-worker-${process.pid}`;
24
+ function isRecord(value) {
25
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
26
+ }
27
+ function stringValue(value) {
28
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
29
+ }
30
+ function numberValue(value) {
31
+ if (typeof value === "number" && Number.isFinite(value)) return value;
32
+ if (typeof value === "string" && value.trim()) {
33
+ const parsed = Number(value);
34
+ return Number.isFinite(parsed) ? parsed : void 0;
35
+ }
36
+ return void 0;
37
+ }
38
+ function booleanValue(value) {
39
+ if (typeof value === "boolean") return value;
40
+ if (typeof value === "string") {
41
+ const normalized = value.trim().toLowerCase();
42
+ if (["true", "1", "yes"].includes(normalized)) return true;
43
+ if (["false", "0", "no"].includes(normalized)) return false;
44
+ }
45
+ return void 0;
46
+ }
47
+ function recordArray(value) {
48
+ if (!Array.isArray(value)) return [];
49
+ return value.slice(0, 5).map((item) => {
50
+ if (isRecord(item)) return item;
51
+ if (typeof item === "string" && item.trim()) return { id: item.trim() };
52
+ return null;
53
+ }).filter((item) => Boolean(item));
54
+ }
55
+ function stringArray(value) {
56
+ if (!Array.isArray(value)) return [];
57
+ return value.map((item) => typeof item === "string" ? item.trim() : void 0).filter((item) => Boolean(item)).slice(0, 5);
58
+ }
59
+ function normalizeAlchemyNetwork(target) {
60
+ const raw = stringValue(target.network) ?? stringValue(target.chain) ?? "ethereum";
61
+ const normalized = raw.toLowerCase();
62
+ const aliases = {
63
+ eth: "eth-mainnet",
64
+ ethereum: "eth-mainnet",
65
+ mainnet: "eth-mainnet",
66
+ base: "base-mainnet",
67
+ polygon: "polygon-mainnet",
68
+ matic: "polygon-mainnet",
69
+ arbitrum: "arb-mainnet",
70
+ arb: "arb-mainnet",
71
+ optimism: "opt-mainnet",
72
+ opt: "opt-mainnet",
73
+ zksync: "zksync-mainnet",
74
+ scroll: "scroll-mainnet",
75
+ linea: "linea-mainnet",
76
+ worldchain: "worldchain-mainnet"
77
+ };
78
+ return aliases[normalized] ?? raw;
79
+ }
80
+ function normalizeNetworkId(target) {
81
+ const explicit = numberValue(target.networkId) ?? numberValue(target.network_id) ?? numberValue(target.chainId) ?? numberValue(target.chain_id);
82
+ if (explicit) return explicit;
83
+ const raw = stringValue(target.network) ?? stringValue(target.chain) ?? "ethereum";
84
+ const aliases = {
85
+ eth: 1,
86
+ ethereum: 1,
87
+ mainnet: 1,
88
+ base: 8453,
89
+ polygon: 137,
90
+ matic: 137,
91
+ arbitrum: 42161,
92
+ arb: 42161,
93
+ optimism: 10,
94
+ opt: 10,
95
+ bsc: 56,
96
+ binance: 56,
97
+ avalanche: 43114,
98
+ avax: 43114,
99
+ linea: 59144,
100
+ scroll: 534352,
101
+ zksync: 324
102
+ };
103
+ return aliases[raw.toLowerCase()] ?? 1;
104
+ }
105
+ function isSolanaWalletTarget(target) {
106
+ const raw = stringValue(target.network) ?? stringValue(target.chain);
107
+ return ["sol", "solana", "svm", "solana-mainnet"].includes(
108
+ raw?.toLowerCase() ?? ""
109
+ );
110
+ }
111
+ function getMonitorTargetAddress(target) {
112
+ return stringValue(target.address) ?? stringValue(target.wallet) ?? stringValue(target.wallet_address) ?? stringValue(target.token_address) ?? stringValue(target.contract);
113
+ }
114
+ function buildWalletWorkflowPlan(monitor) {
115
+ const address = getMonitorTargetAddress(monitor.target);
116
+ if (!address) {
117
+ throw new Error("Wallet monitor target must include address.");
118
+ }
119
+ if (isSolanaWalletTarget(monitor.target)) {
120
+ return [
121
+ {
122
+ toolName: "helius_get_wallet_balances",
123
+ snapshotKey: "wallet_balances",
124
+ args: {
125
+ wallet: address,
126
+ limit: 100,
127
+ showNative: true,
128
+ showNfts: false
129
+ }
130
+ },
131
+ {
132
+ toolName: "helius_get_wallet_transfers",
133
+ snapshotKey: "wallet_transfers",
134
+ args: {
135
+ wallet: address,
136
+ limit: 25
137
+ }
138
+ },
139
+ {
140
+ toolName: "helius_get_wallet_history",
141
+ snapshotKey: "wallet_history",
142
+ args: {
143
+ wallet: address,
144
+ limit: 25
145
+ }
146
+ }
147
+ ];
148
+ }
149
+ const network = normalizeAlchemyNetwork(monitor.target);
150
+ const transferCategories = ["external", "erc20"];
151
+ const fromBlock = stringValue(monitor.target.from_block) ?? "0x0";
152
+ return [
153
+ {
154
+ toolName: "alchemy_get_token_balances_by_wallet",
155
+ snapshotKey: "token_balances",
156
+ args: {
157
+ address,
158
+ network,
159
+ includeNativeTokens: true,
160
+ includeErc20Tokens: true
161
+ }
162
+ },
163
+ {
164
+ toolName: "alchemy_get_native_balance",
165
+ snapshotKey: "native_balance",
166
+ args: { address, network }
167
+ },
168
+ {
169
+ toolName: "alchemy_get_asset_transfers",
170
+ snapshotKey: "outgoing_transfers",
171
+ args: {
172
+ network,
173
+ fromAddress: address,
174
+ fromBlock,
175
+ toBlock: "latest",
176
+ category: transferCategories,
177
+ maxCount: "0x5"
178
+ }
179
+ },
180
+ {
181
+ toolName: "alchemy_get_asset_transfers",
182
+ snapshotKey: "incoming_transfers",
183
+ args: {
184
+ network,
185
+ toAddress: address,
186
+ fromBlock,
187
+ toBlock: "latest",
188
+ category: transferCategories,
189
+ maxCount: "0x5"
190
+ }
191
+ }
192
+ ];
193
+ }
194
+ function buildTokenWorkflowPlan(monitor) {
195
+ const address = getMonitorTargetAddress(monitor.target);
196
+ if (!address) {
197
+ throw new Error("Token monitor target must include address.");
198
+ }
199
+ const network = normalizeAlchemyNetwork(monitor.target);
200
+ const networkId = normalizeNetworkId(monitor.target);
201
+ return [
202
+ {
203
+ toolName: "alchemy_get_token_metadata",
204
+ snapshotKey: "metadata",
205
+ args: { network, address }
206
+ },
207
+ {
208
+ toolName: "alchemy_get_token_prices_by_address",
209
+ snapshotKey: "alchemy_price",
210
+ args: { network, address }
211
+ },
212
+ {
213
+ toolName: "get_token_security",
214
+ snapshotKey: "security",
215
+ args: {
216
+ chainId: String(networkId),
217
+ contract_addresses: address
218
+ }
219
+ },
220
+ {
221
+ toolName: "get_token_price",
222
+ snapshotKey: "dex_price",
223
+ args: { inputs: [{ networkId, address }] }
224
+ },
225
+ {
226
+ toolName: "get_token_holders",
227
+ snapshotKey: "holders",
228
+ args: { networkId, address }
229
+ }
230
+ ];
231
+ }
232
+ function buildProtocolWorkflowPlan(monitor) {
233
+ const protocol = stringValue(monitor.target.protocol) ?? stringValue(monitor.target.slug) ?? stringValue(monitor.target.name);
234
+ if (!protocol) {
235
+ throw new Error("Protocol monitor target must include protocol.");
236
+ }
237
+ return [
238
+ {
239
+ toolName: "get_protocol_tvl",
240
+ snapshotKey: "tvl",
241
+ args: { protocol }
242
+ },
243
+ {
244
+ toolName: "get_protocol_fees",
245
+ snapshotKey: "fees",
246
+ args: { protocol }
247
+ },
248
+ {
249
+ toolName: "get_protocol_fee_summary",
250
+ snapshotKey: "fee_summary",
251
+ args: { protocol }
252
+ },
253
+ {
254
+ toolName: "get_global_defi",
255
+ snapshotKey: "global_defi",
256
+ args: {}
257
+ }
258
+ ];
259
+ }
260
+ function buildMarketWorkflowPlan(monitor) {
261
+ const ids = stringValue(monitor.target.ids) ?? stringValue(monitor.target.id) ?? stringValue(monitor.target.coin_id) ?? stringValue(monitor.target.symbol) ?? stringValue(monitor.target.name);
262
+ if (!ids) {
263
+ throw new Error("Market monitor target must include ids, id, coin_id, symbol, or name.");
264
+ }
265
+ const vsCurrencies = stringValue(monitor.target.vs_currencies) ?? stringValue(monitor.target.vs_currency) ?? "usd";
266
+ const calls = [
267
+ {
268
+ toolName: "get_price",
269
+ snapshotKey: "price",
270
+ args: {
271
+ ids,
272
+ vs_currencies: vsCurrencies,
273
+ include_market_cap: true,
274
+ include_24hr_vol: true,
275
+ include_24hr_change: true,
276
+ include_last_updated_at: true
277
+ }
278
+ }
279
+ ];
280
+ if (booleanValue(monitor.target.include_trending_pools) === true) {
281
+ calls.push({
282
+ toolName: "get_trending_pools",
283
+ snapshotKey: "trending_pools",
284
+ args: {
285
+ duration: stringValue(monitor.target.trending_duration) ?? "24h",
286
+ include: "base_token,quote_token,dex,network",
287
+ page: numberValue(monitor.target.trending_page) ?? 1
288
+ }
289
+ });
290
+ }
291
+ return calls;
292
+ }
293
+ function buildPredictionMarketWorkflowPlan(monitor) {
294
+ const marketId = stringValue(monitor.target.marketId) ?? stringValue(monitor.target.market_id) ?? stringValue(monitor.target.id);
295
+ if (marketId) {
296
+ return [
297
+ {
298
+ toolName: "codex_prediction_market_price",
299
+ snapshotKey: "market_price",
300
+ args: { marketId }
301
+ },
302
+ {
303
+ toolName: "codex_prediction_market_stats",
304
+ snapshotKey: "market_stats",
305
+ args: {
306
+ marketId,
307
+ bucketCount: numberValue(monitor.target.bucket_count) ?? 6,
308
+ durations: ["hour1", "day1"]
309
+ }
310
+ }
311
+ ];
312
+ }
313
+ const phrase = stringValue(monitor.target.phrase) ?? stringValue(monitor.target.query) ?? stringValue(monitor.target.title);
314
+ if (!phrase) {
315
+ throw new Error("Prediction-market monitor target must include marketId or phrase.");
316
+ }
317
+ return [
318
+ {
319
+ toolName: "codex_filter_prediction_markets",
320
+ snapshotKey: "market_search",
321
+ args: {
322
+ phrase,
323
+ limit: numberValue(monitor.target.limit) ?? 10,
324
+ offset: numberValue(monitor.target.offset) ?? 0
325
+ }
326
+ }
327
+ ];
328
+ }
329
+ function buildWatchlistDigestWorkflowPlan(monitor) {
330
+ const calls = [];
331
+ recordArray(monitor.target.wallets).slice(0, 2).forEach((wallet, index) => {
332
+ const target = { ...wallet };
333
+ if (!getMonitorTargetAddress(target) && typeof wallet.id === "string") {
334
+ target.address = wallet.id;
335
+ }
336
+ buildWalletWorkflowPlan({ target }).slice(0, 2).forEach((call) => {
337
+ calls.push({
338
+ ...call,
339
+ snapshotKey: `wallet_${index}_${call.snapshotKey}`
340
+ });
341
+ });
342
+ });
343
+ recordArray(monitor.target.tokens).slice(0, 2).forEach((token, index) => {
344
+ const target = { ...token };
345
+ if (!getMonitorTargetAddress(target) && typeof token.id === "string") {
346
+ target.address = token.id;
347
+ }
348
+ buildTokenWorkflowPlan({ target }).slice(0, 3).forEach((call) => {
349
+ calls.push({
350
+ ...call,
351
+ snapshotKey: `token_${index}_${call.snapshotKey}`
352
+ });
353
+ });
354
+ });
355
+ recordArray(monitor.target.protocols).slice(0, 2).forEach((protocol, index) => {
356
+ const target = { ...protocol };
357
+ if (!stringValue(target.protocol) && typeof protocol.id === "string") {
358
+ target.protocol = protocol.id;
359
+ }
360
+ buildProtocolWorkflowPlan({ target }).slice(0, 2).forEach((call) => {
361
+ calls.push({
362
+ ...call,
363
+ snapshotKey: `protocol_${index}_${call.snapshotKey}`
364
+ });
365
+ });
366
+ });
367
+ recordArray(monitor.target.markets).slice(0, 2).forEach((market, index) => {
368
+ const target = { ...market };
369
+ if (!stringValue(target.ids) && typeof market.id === "string") {
370
+ target.ids = market.id;
371
+ }
372
+ buildMarketWorkflowPlan({ target }).slice(0, 1).forEach((call) => {
373
+ calls.push({
374
+ ...call,
375
+ snapshotKey: `market_${index}_${call.snapshotKey}`
376
+ });
377
+ });
378
+ });
379
+ recordArray(monitor.target.prediction_markets).slice(0, 2).forEach((predictionMarket, index) => {
380
+ const target = { ...predictionMarket };
381
+ if (!stringValue(target.marketId) && typeof predictionMarket.id === "string") {
382
+ target.marketId = predictionMarket.id;
383
+ }
384
+ buildPredictionMarketWorkflowPlan({ target }).slice(0, 1).forEach((call) => {
385
+ calls.push({
386
+ ...call,
387
+ snapshotKey: `prediction_market_${index}_${call.snapshotKey}`
388
+ });
389
+ });
390
+ });
391
+ if (calls.length === 0) {
392
+ throw new Error(
393
+ "Watchlist digest target must include at least one wallet, token, protocol, market, or prediction_market."
394
+ );
395
+ }
396
+ return calls.slice(0, 20);
397
+ }
398
+ function buildTokenDiscoveryRiskWorkflowPlan(monitor) {
399
+ const calls = [];
400
+ const networks = stringArray(monitor.target.networks);
401
+ const include = "base_token,quote_token,dex,network";
402
+ if (networks.length === 0) {
403
+ calls.push({
404
+ toolName: "get_new_pools",
405
+ snapshotKey: "new_pools",
406
+ args: { include, page: numberValue(monitor.target.page) ?? 1 }
407
+ });
408
+ } else {
409
+ networks.slice(0, 3).forEach((network, index) => {
410
+ calls.push({
411
+ toolName: "get_new_pools_by_network",
412
+ snapshotKey: `new_pools_${index}`,
413
+ args: { network, include: "base_token,quote_token,dex", page: 1 }
414
+ });
415
+ });
416
+ }
417
+ calls.push({
418
+ toolName: "get_new_coins",
419
+ snapshotKey: "new_coins",
420
+ args: {}
421
+ });
422
+ stringArray(monitor.target.contracts ?? monitor.target.addresses).slice(0, 3).forEach((address, index) => {
423
+ const chainId = String(normalizeNetworkId(monitor.target));
424
+ calls.push({
425
+ toolName: "get_token_security",
426
+ snapshotKey: `token_security_${index}`,
427
+ args: { chainId, contract_addresses: address }
428
+ });
429
+ });
430
+ return calls;
431
+ }
432
+ function buildRiskWatchWorkflowPlan(monitor) {
433
+ const chainId = String(normalizeNetworkId(monitor.target));
434
+ const calls = [];
435
+ stringArray(monitor.target.addresses).slice(0, 5).forEach((address, index) => {
436
+ calls.push({
437
+ toolName: "check_malicious_address",
438
+ snapshotKey: `address_risk_${index}`,
439
+ args: { chainId, address }
440
+ });
441
+ calls.push({
442
+ toolName: "get_wallet_approvals",
443
+ snapshotKey: `wallet_approvals_${index}`,
444
+ args: { chainId, address }
445
+ });
446
+ });
447
+ stringArray(monitor.target.contracts).slice(0, 5).forEach((address, index) => {
448
+ calls.push({
449
+ toolName: "get_token_security",
450
+ snapshotKey: `contract_security_${index}`,
451
+ args: { chainId, contract_addresses: address }
452
+ });
453
+ calls.push({
454
+ toolName: "detect_rugpull",
455
+ snapshotKey: `rugpull_${index}`,
456
+ args: { chain_id: chainId, contract_address: address }
457
+ });
458
+ });
459
+ if (calls.length === 0) {
460
+ throw new Error("Risk-watch target must include addresses or contracts.");
461
+ }
462
+ return calls.slice(0, 20);
463
+ }
464
+ function buildMonitorWorkflowDefinition(monitor) {
465
+ switch (monitor.kind) {
466
+ case "wallet":
467
+ return {
468
+ type: "wallet",
469
+ label: "Wallet",
470
+ target: {
471
+ ...monitor.target,
472
+ address: getMonitorTargetAddress(monitor.target),
473
+ network: isSolanaWalletTarget(monitor.target) ? "solana" : normalizeAlchemyNetwork(monitor.target)
474
+ },
475
+ calls: buildWalletWorkflowPlan(monitor)
476
+ };
477
+ case "token":
478
+ return {
479
+ type: "token",
480
+ label: "Token",
481
+ target: {
482
+ ...monitor.target,
483
+ address: getMonitorTargetAddress(monitor.target),
484
+ network: normalizeAlchemyNetwork(monitor.target),
485
+ networkId: normalizeNetworkId(monitor.target)
486
+ },
487
+ calls: buildTokenWorkflowPlan(monitor)
488
+ };
489
+ case "protocol": {
490
+ const protocol = stringValue(monitor.target.protocol) ?? stringValue(monitor.target.slug) ?? stringValue(monitor.target.name);
491
+ return {
492
+ type: "protocol",
493
+ label: "Protocol",
494
+ target: { ...monitor.target, protocol },
495
+ calls: buildProtocolWorkflowPlan(monitor)
496
+ };
497
+ }
498
+ case "market": {
499
+ const ids = stringValue(monitor.target.ids) ?? stringValue(monitor.target.id) ?? stringValue(monitor.target.coin_id) ?? stringValue(monitor.target.symbol) ?? stringValue(monitor.target.name);
500
+ return {
501
+ type: "market",
502
+ label: "Market",
503
+ target: {
504
+ ...monitor.target,
505
+ ids,
506
+ vs_currencies: stringValue(monitor.target.vs_currencies) ?? stringValue(monitor.target.vs_currency) ?? "usd"
507
+ },
508
+ calls: buildMarketWorkflowPlan(monitor)
509
+ };
510
+ }
511
+ case "prediction_market":
512
+ return {
513
+ type: "prediction_market",
514
+ label: "Prediction market",
515
+ target: monitor.target,
516
+ calls: buildPredictionMarketWorkflowPlan(monitor)
517
+ };
518
+ case "watchlist_digest":
519
+ return {
520
+ type: "watchlist_digest",
521
+ label: "Watchlist digest",
522
+ target: monitor.target,
523
+ calls: buildWatchlistDigestWorkflowPlan(monitor)
524
+ };
525
+ case "token_discovery_risk":
526
+ return {
527
+ type: "token_discovery_risk",
528
+ label: "Token discovery risk",
529
+ target: monitor.target,
530
+ calls: buildTokenDiscoveryRiskWorkflowPlan(monitor)
531
+ };
532
+ case "risk_watch":
533
+ return {
534
+ type: "risk_watch",
535
+ label: "Risk watch",
536
+ target: monitor.target,
537
+ calls: buildRiskWatchWorkflowPlan(monitor)
538
+ };
539
+ }
540
+ }
541
+ function sortedJson(value) {
542
+ if (Array.isArray(value)) return `[${value.map(sortedJson).join(",")}]`;
543
+ if (value && typeof value === "object") {
544
+ return `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => `${JSON.stringify(key)}:${sortedJson(item)}`).join(",")}}`;
545
+ }
546
+ return JSON.stringify(value);
547
+ }
548
+ function stripVolatileFields(value) {
549
+ if (Array.isArray(value)) return value.map(stripVolatileFields);
550
+ if (!isRecord(value)) return value;
551
+ const omitted = /* @__PURE__ */ new Set([
552
+ "_hive",
553
+ "duration_ms",
554
+ "fetched_at",
555
+ "captured_at",
556
+ "created_at",
557
+ "updated_at",
558
+ "cache_status",
559
+ "request_id"
560
+ ]);
561
+ return Object.fromEntries(
562
+ Object.entries(value).filter(([key]) => !omitted.has(key)).map(([key, item]) => [key, stripVolatileFields(item)])
563
+ );
564
+ }
565
+ function compactForStorage(value, depth = 0) {
566
+ if (depth > 8) return "[max-depth]";
567
+ if (typeof value === "string") {
568
+ return value.length > 5e3 ? `${value.slice(0, 5e3)}...[truncated]` : value;
569
+ }
570
+ if (Array.isArray(value)) {
571
+ const items = value.slice(0, 50).map((item) => compactForStorage(item, depth + 1));
572
+ if (value.length > items.length) {
573
+ items.push({ _truncated_items: value.length - items.length });
574
+ }
575
+ return items;
576
+ }
577
+ if (isRecord(value)) {
578
+ return Object.fromEntries(
579
+ Object.entries(value).map(([key, item]) => [
580
+ key,
581
+ compactForStorage(item, depth + 1)
582
+ ])
583
+ );
584
+ }
585
+ return value;
586
+ }
587
+ function fingerprintSnapshot(snapshot) {
588
+ return createHash("sha256").update(sortedJson(stripVolatileFields(snapshot))).digest("hex");
589
+ }
590
+ function computeNextRunAt(cadence, from = /* @__PURE__ */ new Date()) {
591
+ const normalized = (cadence ?? "manual").trim().toLowerCase();
592
+ if (!normalized || normalized === "manual" || normalized === "once") {
593
+ return null;
594
+ }
595
+ const intervalMatch = normalized.match(/^(\d+)\s*(m|min|minute|minutes|h|hr|hour|hours|d|day|days|w|week|weeks)$/);
596
+ const next = new Date(from);
597
+ if (intervalMatch) {
598
+ const amount = Number.parseInt(intervalMatch[1], 10);
599
+ const unit = intervalMatch[2];
600
+ if (unit.startsWith("m")) next.setMinutes(next.getMinutes() + amount);
601
+ else if (unit.startsWith("h")) next.setHours(next.getHours() + amount);
602
+ else if (unit.startsWith("d")) next.setDate(next.getDate() + amount);
603
+ else next.setDate(next.getDate() + amount * 7);
604
+ return next.toISOString();
605
+ }
606
+ if (normalized === "hourly") next.setHours(next.getHours() + 1);
607
+ else if (normalized === "daily") next.setDate(next.getDate() + 1);
608
+ else if (normalized === "weekly") next.setDate(next.getDate() + 7);
609
+ else return null;
610
+ return next.toISOString();
611
+ }
612
+ function hasAlertRule(monitor) {
613
+ return monitor.rules.some(
614
+ (rule) => isRecord(rule) && (rule.alert === true || rule.notify === true || [
615
+ "any_change",
616
+ "wallet_change",
617
+ "large_transfer",
618
+ "token_change",
619
+ "protocol_change",
620
+ "market_change",
621
+ "prediction_market_change"
622
+ ].includes(String(rule.type ?? "")))
623
+ );
624
+ }
625
+ var RULE_SEVERITY_ORDER = {
626
+ low: 1,
627
+ medium: 2,
628
+ high: 3,
629
+ critical: 4
630
+ };
631
+ function ruleType(rule) {
632
+ if (!isRecord(rule)) return void 0;
633
+ return stringValue(rule.type) ?? stringValue(rule.metric);
634
+ }
635
+ function ruleThreshold(rule) {
636
+ if (!isRecord(rule)) return void 0;
637
+ return numberValue(rule.threshold) ?? numberValue(rule.threshold_pct) ?? numberValue(rule.min_value) ?? numberValue(rule.value);
638
+ }
639
+ function collectNumbersByKey(value, keyPatterns) {
640
+ const found = [];
641
+ const visit = (item, key = "") => {
642
+ if (found.length >= 100) return;
643
+ if (typeof item === "number" && Number.isFinite(item)) {
644
+ if (keyPatterns.some((pattern) => pattern.test(key))) found.push(item);
645
+ return;
646
+ }
647
+ if (typeof item === "string" && item.trim()) {
648
+ const parsed = Number(item.replace(/[$,%]/g, ""));
649
+ if (Number.isFinite(parsed) && keyPatterns.some((pattern) => pattern.test(key))) {
650
+ found.push(parsed);
651
+ }
652
+ return;
653
+ }
654
+ if (Array.isArray(item)) {
655
+ item.forEach((child) => visit(child, key));
656
+ return;
657
+ }
658
+ if (isRecord(item)) {
659
+ Object.entries(item).forEach(([childKey, child]) => visit(child, childKey));
660
+ }
661
+ };
662
+ visit(value);
663
+ return found;
664
+ }
665
+ function maxAbsPercentChange(previous, current) {
666
+ const count = Math.min(previous.length, current.length);
667
+ let maxChange = null;
668
+ for (let index = 0; index < count; index++) {
669
+ const before = previous[index];
670
+ const after = current[index];
671
+ if (!Number.isFinite(before) || !Number.isFinite(after) || before === 0) continue;
672
+ const change = Math.abs((after - before) / before * 100);
673
+ maxChange = maxChange === null ? change : Math.max(maxChange, change);
674
+ }
675
+ return maxChange;
676
+ }
677
+ function thresholdSeverity(value, threshold) {
678
+ if (value >= threshold * 5) return "critical";
679
+ if (value >= threshold * 3) return "high";
680
+ if (value >= threshold * 1.5) return "medium";
681
+ return "low";
682
+ }
683
+ function detectRiskSeverity(snapshot) {
684
+ const text = sortedJson(snapshot).toLowerCase();
685
+ if (/\bcritical\b|is_malicious[^a-z0-9]*true|honeypot[^a-z0-9]*true|rugpull[^a-z0-9]*true/.test(text)) {
686
+ return "critical";
687
+ }
688
+ if (/\bhigh\b|blacklist[^a-z0-9]*true|phishing[^a-z0-9]*true/.test(text)) {
689
+ return "high";
690
+ }
691
+ if (/\bmedium\b|warning|suspicious/.test(text)) return "medium";
692
+ if (/\blow\b/.test(text)) return "low";
693
+ return null;
694
+ }
695
+ function severityAtLeast(actual, minimum) {
696
+ return RULE_SEVERITY_ORDER[actual] >= RULE_SEVERITY_ORDER[minimum];
697
+ }
698
+ function evaluateMonitorRules(args) {
699
+ const evaluations = [];
700
+ for (const rule of args.monitor.rules) {
701
+ const type = ruleType(rule);
702
+ if (!type) continue;
703
+ if (type === "risk_severity_at_least") {
704
+ const minimum = isRecord(rule) && stringValue(rule.severity) || isRecord(rule) && stringValue(rule.minimum) || "high";
705
+ if (!["low", "medium", "high", "critical"].includes(minimum)) continue;
706
+ const actual = detectRiskSeverity(args.currentSnapshot);
707
+ if (actual && severityAtLeast(actual, minimum)) {
708
+ evaluations.push({
709
+ kind: "rule_risk_severity_at_least",
710
+ severity: actual,
711
+ title: `${args.monitor.name} matched risk severity rule`,
712
+ evidence: { rule, actual_severity: actual, minimum_severity: minimum }
713
+ });
714
+ }
715
+ continue;
716
+ }
717
+ const threshold = ruleThreshold(rule);
718
+ if (threshold === void 0 || threshold <= 0) continue;
719
+ const patternsByType = {
720
+ price_change_pct: [/price/i, /usd/i],
721
+ tvl_change_pct: [/tvl/i, /liquidity/i, /totalLiquidityUSD/i],
722
+ fee_change_pct: [/fee/i, /fees/i, /total24h/i, /total7d/i],
723
+ liquidity_change_pct: [/liquidity/i, /reserve.*usd/i, /totalLiquidityUSD/i],
724
+ transfer_value_usd: [/value.*usd/i, /amount.*usd/i, /usd/i]
725
+ };
726
+ const patterns = patternsByType[type];
727
+ if (!patterns) continue;
728
+ const currentNumbers = collectNumbersByKey(args.currentSnapshot, patterns);
729
+ if (type === "transfer_value_usd") {
730
+ const maxValue = Math.max(...currentNumbers, 0);
731
+ if (maxValue >= threshold) {
732
+ evaluations.push({
733
+ kind: "rule_transfer_value_usd",
734
+ severity: thresholdSeverity(maxValue, threshold),
735
+ title: `${args.monitor.name} matched transfer value rule`,
736
+ evidence: { rule, max_value_usd: maxValue, threshold }
737
+ });
738
+ }
739
+ continue;
740
+ }
741
+ if (!args.previousSnapshot) continue;
742
+ const previousNumbers = collectNumbersByKey(args.previousSnapshot, patterns);
743
+ const maxChange = maxAbsPercentChange(previousNumbers, currentNumbers);
744
+ if (maxChange !== null && maxChange >= threshold) {
745
+ evaluations.push({
746
+ kind: `rule_${type}`,
747
+ severity: thresholdSeverity(maxChange, threshold),
748
+ title: `${args.monitor.name} matched ${type} rule`,
749
+ evidence: { rule, max_change_pct: maxChange, threshold }
750
+ });
751
+ }
752
+ }
753
+ return evaluations;
754
+ }
755
+ async function executeMonitorWorkflow(monitor, executeTool, now) {
756
+ const definition = buildMonitorWorkflowDefinition(monitor);
757
+ const results = {};
758
+ const toolCalls = [];
759
+ const failedCalls = [];
760
+ for (const call of definition.calls) {
761
+ const result = await executeTool(call.toolName, call.args);
762
+ const record = {
763
+ tool: call.toolName,
764
+ args: call.args,
765
+ ok: result.ok,
766
+ duration_ms: result.durationMs,
767
+ runtime_status: result.meta.runtime_status,
768
+ ...result.error && { error: result.error.message }
769
+ };
770
+ toolCalls.push(record);
771
+ if (!result.ok) failedCalls.push(record);
772
+ results[call.snapshotKey] = result.ok ? compactForStorage(result.data) : { error: result.error?.message ?? "Tool call failed" };
773
+ }
774
+ const snapshot = {
775
+ type: definition.type,
776
+ monitor_id: monitor.id,
777
+ captured_at: now.toISOString(),
778
+ target: definition.target,
779
+ results
780
+ };
781
+ return {
782
+ type: definition.type,
783
+ label: definition.label,
784
+ snapshot,
785
+ fingerprint: fingerprintSnapshot(snapshot),
786
+ toolCalls,
787
+ failedCalls
788
+ };
789
+ }
790
+ var SupabaseMonitorRunnerStore = class {
791
+ supabase() {
792
+ if (!isSupabaseConfigured()) {
793
+ throw new Error(
794
+ "Supabase is not configured. Set SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY."
795
+ );
796
+ }
797
+ return getSupabaseAdmin();
798
+ }
799
+ async claimDueMonitors(args) {
800
+ const { data, error } = await this.supabase().rpc("claim_due_monitors", {
801
+ batch_limit: args.batchSize,
802
+ worker_id: args.workerId,
803
+ lock_timeout_seconds: args.lockTimeoutSeconds
804
+ });
805
+ if (error) {
806
+ secureLogger.error("claim_due_monitors failed", error, {
807
+ workerId: args.workerId
808
+ });
809
+ return {
810
+ ok: false,
811
+ code: "STATE_BACKEND_UNAVAILABLE",
812
+ message: `Failed to claim due monitors: ${error.message}`
813
+ };
814
+ }
815
+ return { ok: true, data: data ?? [] };
816
+ }
817
+ async createRun(args) {
818
+ const { data, error } = await this.supabase().from("monitor_runs").insert({
819
+ monitor_id: args.monitor.id,
820
+ user_id: args.monitor.user_id,
821
+ subject_id: args.monitor.subject_id,
822
+ status: "running",
823
+ source: args.source,
824
+ metadata: { worker_id: args.workerId }
825
+ }).select("*").single();
826
+ if (error) throw new Error(`Failed to create monitor run: ${error.message}`);
827
+ return data;
828
+ }
829
+ async getLatestSnapshot(monitorId) {
830
+ const { data, error } = await this.supabase().from("monitor_snapshots").select("*").eq("monitor_id", monitorId).order("created_at", { ascending: false }).limit(1).maybeSingle();
831
+ if (error) throw new Error(`Failed to fetch latest snapshot: ${error.message}`);
832
+ return data ?? null;
833
+ }
834
+ async writeSnapshot(args) {
835
+ const { error } = await this.supabase().from("monitor_snapshots").insert({
836
+ monitor_id: args.monitor.id,
837
+ user_id: args.monitor.user_id,
838
+ subject_id: args.monitor.subject_id,
839
+ run_id: args.runId,
840
+ snapshot: args.snapshot,
841
+ fingerprint: args.fingerprint
842
+ });
843
+ if (error && error.code !== "23505") {
844
+ throw new Error(`Failed to write snapshot: ${error.message}`);
845
+ }
846
+ }
847
+ async writeObservation(args) {
848
+ const { error } = await this.supabase().from("observations").insert({
849
+ monitor_id: args.monitor.id,
850
+ user_id: args.monitor.user_id,
851
+ subject_id: args.monitor.subject_id,
852
+ run_id: args.runId,
853
+ kind: args.kind,
854
+ severity: args.severity,
855
+ title: args.title,
856
+ evidence: args.evidence
857
+ });
858
+ if (error) throw new Error(`Failed to write observation: ${error.message}`);
859
+ }
860
+ async writeAlert(args) {
861
+ const { error } = await this.supabase().from("alerts").insert({
862
+ monitor_id: args.monitor.id,
863
+ user_id: args.monitor.user_id,
864
+ subject_id: args.monitor.subject_id,
865
+ severity: args.severity,
866
+ title: args.title,
867
+ body: args.body,
868
+ payload: args.payload
869
+ });
870
+ if (error) throw new Error(`Failed to write alert: ${error.message}`);
871
+ }
872
+ async finishRun(args) {
873
+ const { error } = await this.supabase().from("monitor_runs").update({
874
+ status: args.status,
875
+ completed_at: (/* @__PURE__ */ new Date()).toISOString(),
876
+ tool_calls: args.toolCalls,
877
+ summary: args.summary,
878
+ error_code: args.errorCode ?? null,
879
+ error_message: args.errorMessage ?? null,
880
+ metadata: args.metadata ?? {}
881
+ }).eq("id", args.runId);
882
+ if (error) throw new Error(`Failed to finish monitor run: ${error.message}`);
883
+ }
884
+ async releaseMonitor(args) {
885
+ const { error } = await this.supabase().from("monitors").update({
886
+ last_run_at: args.lastRunAt,
887
+ next_run_at: args.nextRunAt,
888
+ locked_at: null,
889
+ locked_by: null,
890
+ ...args.status && { status: args.status }
891
+ }).eq("id", args.monitor.id).eq("user_id", args.monitor.user_id).eq("subject_id", args.monitor.subject_id);
892
+ if (error) throw new Error(`Failed to release monitor: ${error.message}`);
893
+ }
894
+ };
895
+ async function processMonitor(monitor, options) {
896
+ const run = await options.store.createRun({
897
+ monitor,
898
+ workerId: options.workerId,
899
+ source: options.source
900
+ });
901
+ const completedAt = options.now.toISOString();
902
+ const nextRunAt = computeNextRunAt(monitor.cadence, options.now);
903
+ try {
904
+ const previous = await options.store.getLatestSnapshot(monitor.id);
905
+ const workflow = await executeMonitorWorkflow(
906
+ monitor,
907
+ options.executeTool,
908
+ options.now
909
+ );
910
+ await options.store.writeSnapshot({
911
+ monitor,
912
+ runId: run.id,
913
+ snapshot: workflow.snapshot,
914
+ fingerprint: workflow.fingerprint
915
+ });
916
+ const ruleEvaluations = evaluateMonitorRules({
917
+ monitor,
918
+ previousSnapshot: previous?.snapshot ?? null,
919
+ currentSnapshot: workflow.snapshot
920
+ });
921
+ let observations = 0;
922
+ let alerts = 0;
923
+ const observationPrefix = workflow.type;
924
+ let summary = `${workflow.label} monitor completed with no material change.`;
925
+ if (!previous) {
926
+ summary = `${workflow.label} monitor baseline snapshot recorded.`;
927
+ await options.store.writeObservation({
928
+ monitor,
929
+ runId: run.id,
930
+ kind: `${observationPrefix}_baseline`,
931
+ severity: "info",
932
+ title: `Baseline recorded for ${monitor.name}`,
933
+ evidence: { fingerprint: workflow.fingerprint }
934
+ });
935
+ observations++;
936
+ } else if (previous.fingerprint !== workflow.fingerprint) {
937
+ summary = `${workflow.label} monitor detected a snapshot change.`;
938
+ const evidence = {
939
+ previous_fingerprint: previous.fingerprint,
940
+ current_fingerprint: workflow.fingerprint,
941
+ failed_calls: workflow.failedCalls.map((call) => call.tool)
942
+ };
943
+ await options.store.writeObservation({
944
+ monitor,
945
+ runId: run.id,
946
+ kind: `${observationPrefix}_change`,
947
+ severity: workflow.failedCalls.length > 0 ? "medium" : "low",
948
+ title: `${workflow.label} monitor changed: ${monitor.name}`,
949
+ evidence
950
+ });
951
+ observations++;
952
+ if (hasAlertRule(monitor)) {
953
+ await options.store.writeAlert({
954
+ monitor,
955
+ severity: workflow.failedCalls.length > 0 ? "medium" : "low",
956
+ title: `${workflow.label} monitor changed: ${monitor.name}`,
957
+ body: `Hive detected a changed ${workflow.type} snapshot for this monitor.`,
958
+ payload: evidence
959
+ });
960
+ alerts++;
961
+ }
962
+ }
963
+ for (const evaluation of ruleEvaluations) {
964
+ await options.store.writeObservation({
965
+ monitor,
966
+ runId: run.id,
967
+ kind: evaluation.kind,
968
+ severity: evaluation.severity,
969
+ title: evaluation.title,
970
+ evidence: evaluation.evidence
971
+ });
972
+ observations++;
973
+ await options.store.writeAlert({
974
+ monitor,
975
+ severity: evaluation.severity,
976
+ title: evaluation.title,
977
+ body: "Hive matched a deterministic monitor rule threshold.",
978
+ payload: evaluation.evidence
979
+ });
980
+ alerts++;
981
+ }
982
+ if (workflow.failedCalls.length > 0) {
983
+ await options.store.writeObservation({
984
+ monitor,
985
+ runId: run.id,
986
+ kind: `${observationPrefix}_monitor_degraded`,
987
+ severity: "medium",
988
+ title: `${workflow.label} monitor had ${workflow.failedCalls.length} failed tool call(s)`,
989
+ evidence: {
990
+ failed_calls: workflow.failedCalls.map((call) => ({
991
+ tool: call.tool,
992
+ error: call.error
993
+ }))
994
+ }
995
+ });
996
+ observations++;
997
+ }
998
+ await options.store.finishRun({
999
+ runId: run.id,
1000
+ status: "completed",
1001
+ summary,
1002
+ toolCalls: workflow.toolCalls,
1003
+ metadata: {
1004
+ fingerprint: workflow.fingerprint,
1005
+ failed_call_count: workflow.failedCalls.length,
1006
+ rule_evaluation_count: ruleEvaluations.length
1007
+ }
1008
+ });
1009
+ await options.store.releaseMonitor({
1010
+ monitor,
1011
+ lastRunAt: completedAt,
1012
+ nextRunAt
1013
+ });
1014
+ return {
1015
+ monitorId: monitor.id,
1016
+ kind: monitor.kind,
1017
+ status: "completed",
1018
+ runId: run.id,
1019
+ summary,
1020
+ nextRunAt,
1021
+ observations,
1022
+ alerts
1023
+ };
1024
+ } catch (error) {
1025
+ const message = error instanceof Error ? error.message : String(error);
1026
+ await options.store.finishRun({
1027
+ runId: run.id,
1028
+ status: "failed",
1029
+ summary: "Monitor run failed.",
1030
+ toolCalls: [],
1031
+ errorCode: "MONITOR_RUN_FAILED",
1032
+ errorMessage: message
1033
+ });
1034
+ await options.store.releaseMonitor({
1035
+ monitor,
1036
+ lastRunAt: completedAt,
1037
+ nextRunAt
1038
+ });
1039
+ return {
1040
+ monitorId: monitor.id,
1041
+ kind: monitor.kind,
1042
+ status: "failed",
1043
+ runId: run.id,
1044
+ summary: message,
1045
+ nextRunAt,
1046
+ observations: 0,
1047
+ alerts: 0
1048
+ };
1049
+ }
1050
+ }
1051
+ async function runDueMonitors(options = {}) {
1052
+ const store = options.store ?? new SupabaseMonitorRunnerStore();
1053
+ const workerId = options.workerId ?? DEFAULT_WORKER_ID;
1054
+ const batchSize = Math.max(1, Math.min(options.batchSize ?? DEFAULT_BATCH_SIZE, 100));
1055
+ const lockTimeoutSeconds = options.lockTimeoutSeconds ?? DEFAULT_LOCK_TIMEOUT_SECONDS;
1056
+ const now = options.now ?? /* @__PURE__ */ new Date();
1057
+ const executeTool = options.executeTool ?? executeToolByName;
1058
+ const source = options.source ?? "scheduled";
1059
+ const claimed = await store.claimDueMonitors({
1060
+ batchSize,
1061
+ workerId,
1062
+ lockTimeoutSeconds
1063
+ });
1064
+ if (!claimed.ok) {
1065
+ return {
1066
+ ok: false,
1067
+ claimed: 0,
1068
+ completed: 0,
1069
+ failed: 0,
1070
+ skipped: 0,
1071
+ results: [],
1072
+ error: claimed.message
1073
+ };
1074
+ }
1075
+ const results = [];
1076
+ for (const monitor of claimed.data) {
1077
+ results.push(
1078
+ await processMonitor(monitor, {
1079
+ store,
1080
+ workerId,
1081
+ now,
1082
+ source,
1083
+ executeTool
1084
+ })
1085
+ );
1086
+ }
1087
+ return {
1088
+ ok: results.every((result) => result.status !== "failed"),
1089
+ claimed: claimed.data.length,
1090
+ completed: results.filter((result) => result.status === "completed").length,
1091
+ failed: results.filter((result) => result.status === "failed").length,
1092
+ skipped: results.filter((result) => result.status === "skipped").length,
1093
+ results
1094
+ };
1095
+ }
1096
+
1097
+ // src/monitor-worker.ts
1098
+ function intEnv(name, fallback) {
1099
+ const parsed = Number.parseInt(process.env[name] ?? "", 10);
1100
+ return Number.isFinite(parsed) ? parsed : fallback;
1101
+ }
1102
+ async function runMonitorWorkerFromEnv() {
1103
+ try {
1104
+ const result = await runDueMonitors({
1105
+ batchSize: intEnv("HIVE_MONITOR_WORKER_BATCH_SIZE", 10),
1106
+ workerId: process.env.HIVE_MONITOR_WORKER_ID,
1107
+ lockTimeoutSeconds: intEnv("HIVE_MONITOR_LOCK_TIMEOUT_SECONDS", 300)
1108
+ });
1109
+ console.log(
1110
+ JSON.stringify(
1111
+ {
1112
+ service: "hive-monitor-worker",
1113
+ worker_id: process.env.HIVE_MONITOR_WORKER_ID ?? null,
1114
+ ...result
1115
+ },
1116
+ null,
1117
+ 2
1118
+ )
1119
+ );
1120
+ if (!result.ok) process.exitCode = 1;
1121
+ } finally {
1122
+ await closeRedisClient();
1123
+ }
1124
+ }
1125
+ var invokedPath = process.argv[1] ? resolve(process.argv[1]) : "";
1126
+ if (invokedPath === fileURLToPath(import.meta.url)) {
1127
+ runMonitorWorkerFromEnv().catch((error) => {
1128
+ console.error(
1129
+ JSON.stringify({
1130
+ service: "hive-monitor-worker",
1131
+ ok: false,
1132
+ error: error instanceof Error ? error.message : String(error)
1133
+ })
1134
+ );
1135
+ process.exitCode = 1;
1136
+ });
1137
+ }
1138
+ export {
1139
+ runMonitorWorkerFromEnv
1140
+ };