@finchagentic/mcp 4.7.1 → 4.7.3
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 +74 -39
- package/dist/_zod-helpers.js +10 -0
- package/dist/cli-doctor.js +300 -0
- package/dist/cli-env.js +91 -0
- package/dist/cli-install.js +236 -0
- package/dist/cli-login.js +101 -0
- package/dist/cli-orders.js +175 -0
- package/dist/cli-setup.js +270 -0
- package/dist/cli-ui.js +168 -0
- package/dist/cli-vault.js +75 -0
- package/dist/cli.js +61 -1152
- package/dist/config.js +3 -2
- package/dist/enrichment-router.js +5 -18
- package/dist/finch-output.js +30 -29
- package/dist/finch-status.js +105 -40
- package/dist/index.js +0 -0
- package/dist/local-vault.js +5 -12
- package/dist/output-schemas.js +3 -12
- package/dist/project.js +4 -13
- package/dist/server.js +13 -35
- package/dist/tool-filter.js +4 -13
- package/dist/tools/_solidity-scan.js +5 -14
- package/dist/tools/agents.js +14 -26
- package/dist/tools/deep-research-constants.js +33 -0
- package/dist/tools/deep-research-firecrawl.js +88 -0
- package/dist/tools/deep-research-planning.js +249 -0
- package/dist/tools/deep-research-synthesis.js +343 -0
- package/dist/tools/deep-research-text.js +158 -0
- package/dist/tools/deep-research-tools.js +90 -0
- package/dist/tools/deep-research.js +47 -938
- package/dist/tools/defi.js +4 -3
- package/dist/tools/insider.js +4 -14
- package/dist/tools/insight.js +7 -6
- package/dist/tools/market.js +16 -15
- package/dist/tools/memory.js +57 -62
- package/dist/tools/monitor.js +7 -6
- package/dist/tools/os.js +7 -85
- package/dist/tools/research.js +7 -6
- package/dist/tools/rh-mcp-constants.js +65 -0
- package/dist/tools/rh-mcp-dex.js +107 -0
- package/dist/tools/rh-mcp-provider.js +127 -0
- package/dist/tools/rh-mcp-resolve.js +137 -0
- package/dist/tools/rh-mcp-risk.js +230 -0
- package/dist/tools/rh-mcp-safety.js +234 -0
- package/dist/tools/rh-mcp-swap.js +181 -0
- package/dist/tools/rh-mcp-tools.js +137 -0
- package/dist/tools/rh-mcp.js +75 -1191
- package/dist/tools/scanner.js +10 -9
- package/dist/tools/stake.js +7 -20
- package/dist/tools/vault.js +52 -59
- package/package.json +12 -9
package/dist/tools/defi.js
CHANGED
|
@@ -7,6 +7,7 @@ const zod_1 = require("zod");
|
|
|
7
7
|
const convex_js_1 = require("../convex.js");
|
|
8
8
|
const wallet_js_1 = require("../wallet.js");
|
|
9
9
|
const token_decimals_js_1 = require("../token-decimals.js");
|
|
10
|
+
const _zod_helpers_js_1 = require("../_zod-helpers.js");
|
|
10
11
|
exports.DEFI_TOOLS = [
|
|
11
12
|
{
|
|
12
13
|
name: "get_defi_yields",
|
|
@@ -223,9 +224,9 @@ async function handleDefiTool(name, args) {
|
|
|
223
224
|
// list either, so this case was already unreachable dead code - no Tool
|
|
224
225
|
// registers the name "analyze_wallet" for the MCP dispatcher to route to.
|
|
225
226
|
case "get_defi_yields": {
|
|
226
|
-
const parsed =
|
|
227
|
-
if (!parsed.
|
|
228
|
-
return
|
|
227
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(DefiYieldsSchema, args ?? {});
|
|
228
|
+
if (!parsed.ok)
|
|
229
|
+
return parsed.error;
|
|
229
230
|
const { token, minApy = 1, limit = 20 } = parsed.data;
|
|
230
231
|
let pools;
|
|
231
232
|
try {
|
package/dist/tools/insider.js
CHANGED
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// Insider transactions from SEC Form 4
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// 1. A company's filing feed contains Form 4s where that company is the
|
|
8
|
-
// REPORTING OWNER of a stake in some other issuer. Those are not insider
|
|
9
|
-
// trades in the ticker you asked about, so every filing is checked against
|
|
10
|
-
// `issuerTradingSymbol` before it counts.
|
|
11
|
-
//
|
|
12
|
-
// 2. Most "insider selling" is transaction code F — shares withheld to cover
|
|
13
|
-
// tax on vesting RSUs. It is automatic, not a decision, and carries no
|
|
14
|
-
// signal. Only P (open-market purchase) and S (open-market sale) reflect a
|
|
15
|
-
// choice, so they are reported separately from everything else.
|
|
2
|
+
// Insider transactions from SEC Form 4. Two traps handled: (1) filings
|
|
3
|
+
// where the company is REPORTING OWNER of another issuer are filtered via
|
|
4
|
+
// `issuerTradingSymbol`; (2) code F (tax withholding on RSU vests, no real
|
|
5
|
+
// signal) is separated from P/S (actual buy/sell decisions).
|
|
16
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
7
|
exports.INSIDER_TOOLS = void 0;
|
|
18
8
|
exports.buildInsiderSummary = buildInsiderSummary;
|
package/dist/tools/insight.js
CHANGED
|
@@ -8,6 +8,7 @@ const llm_js_1 = require("../llm.js");
|
|
|
8
8
|
const memory_js_1 = require("./memory.js");
|
|
9
9
|
const enrichment_router_js_1 = require("../enrichment-router.js");
|
|
10
10
|
const signal_gate_js_1 = require("../signal-gate.js");
|
|
11
|
+
const _zod_helpers_js_1 = require("../_zod-helpers.js");
|
|
11
12
|
exports.INSIGHT_TOOLS = [
|
|
12
13
|
{
|
|
13
14
|
name: "ask_finch",
|
|
@@ -449,9 +450,9 @@ async function handleInsightTool(name, args) {
|
|
|
449
450
|
return { content: [{ type: "text", text: contextHeader + finalAnswer }] };
|
|
450
451
|
}
|
|
451
452
|
if (name === "market_thesis") {
|
|
452
|
-
const parsed =
|
|
453
|
-
if (!parsed.
|
|
454
|
-
return
|
|
453
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(MarketThesisSchema, args);
|
|
454
|
+
if (!parsed.ok)
|
|
455
|
+
return parsed.error;
|
|
455
456
|
const { token, context } = parsed.data;
|
|
456
457
|
const priceData = await fetchVerifiedPrice(token);
|
|
457
458
|
// Hard guard: without a verified live price, refuse to generate. Better
|
|
@@ -517,9 +518,9 @@ async function handleInsightTool(name, args) {
|
|
|
517
518
|
};
|
|
518
519
|
}
|
|
519
520
|
if (name === "trade_plan") {
|
|
520
|
-
const parsed =
|
|
521
|
-
if (!parsed.
|
|
522
|
-
return
|
|
521
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(TradePlanSchema, args);
|
|
522
|
+
if (!parsed.ok)
|
|
523
|
+
return parsed.error;
|
|
523
524
|
const { token, side = "long", portfolioSize, riskTolerance = "moderate", timeframe } = parsed.data;
|
|
524
525
|
const priceData = await fetchVerifiedPrice(token);
|
|
525
526
|
// Hard guard: trade plans without verified live price = entry/SL/TP
|
package/dist/tools/market.js
CHANGED
|
@@ -11,6 +11,7 @@ exports.handleMarketTool = handleMarketTool;
|
|
|
11
11
|
const zod_1 = require("zod");
|
|
12
12
|
const _http_cache_js_1 = require("../_http-cache.js");
|
|
13
13
|
const dex_pair_js_1 = require("../dex-pair.js");
|
|
14
|
+
const _zod_helpers_js_1 = require("../_zod-helpers.js");
|
|
14
15
|
const COINGECKO = "https://api.coingecko.com/api/v3";
|
|
15
16
|
const SYMBOL_TO_ID = {
|
|
16
17
|
BTC: "bitcoin", ETH: "ethereum", SOL: "solana", BNB: "binancecoin",
|
|
@@ -291,9 +292,9 @@ async function fetchMarketSnapshot() {
|
|
|
291
292
|
async function handleMarketTool(name, args) {
|
|
292
293
|
switch (name) {
|
|
293
294
|
case "get_market_data": {
|
|
294
|
-
const parsed =
|
|
295
|
-
if (!parsed.
|
|
296
|
-
return
|
|
295
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(GetMarketDataSchema, args ?? {});
|
|
296
|
+
if (!parsed.ok)
|
|
297
|
+
return parsed.error;
|
|
297
298
|
const { token } = parsed.data;
|
|
298
299
|
if (token) {
|
|
299
300
|
const resolved = await resolveTokenId(token);
|
|
@@ -347,9 +348,9 @@ async function handleMarketTool(name, args) {
|
|
|
347
348
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
348
349
|
}
|
|
349
350
|
case "get_token_data": {
|
|
350
|
-
const parsed =
|
|
351
|
-
if (!parsed.
|
|
352
|
-
return
|
|
351
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(GetTokenDataSchema, args);
|
|
352
|
+
if (!parsed.ok)
|
|
353
|
+
return parsed.error;
|
|
353
354
|
const q = parsed.data.question;
|
|
354
355
|
// Try to extract a known symbol first, then fall back to search
|
|
355
356
|
const upperQ = q.toUpperCase();
|
|
@@ -379,9 +380,9 @@ async function handleMarketTool(name, args) {
|
|
|
379
380
|
};
|
|
380
381
|
}
|
|
381
382
|
case "compare_tokens": {
|
|
382
|
-
const parsed =
|
|
383
|
-
if (!parsed.
|
|
384
|
-
return
|
|
383
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(CompareTokensSchema, args);
|
|
384
|
+
if (!parsed.ok)
|
|
385
|
+
return parsed.error;
|
|
385
386
|
const syms = parsed.data.tokens.map(t => t.toUpperCase());
|
|
386
387
|
const ids = syms.map(s => SYMBOL_TO_ID[s]).filter(Boolean);
|
|
387
388
|
const unknown = syms.filter(s => !SYMBOL_TO_ID[s]);
|
|
@@ -447,9 +448,9 @@ async function handleMarketTool(name, args) {
|
|
|
447
448
|
return { content: [{ type: "text", text: lines.join("\n") }], structuredContent: buildMarketOverview(global, fg, trendCoins) };
|
|
448
449
|
}
|
|
449
450
|
case "token_history": {
|
|
450
|
-
const parsed =
|
|
451
|
-
if (!parsed.
|
|
452
|
-
return
|
|
451
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(TokenHistorySchema, args);
|
|
452
|
+
if (!parsed.ok)
|
|
453
|
+
return parsed.error;
|
|
453
454
|
const days = parsed.data.days ?? 7;
|
|
454
455
|
const resolved = await resolveTokenId(parsed.data.token);
|
|
455
456
|
if (!resolved)
|
|
@@ -496,9 +497,9 @@ async function handleMarketTool(name, args) {
|
|
|
496
497
|
};
|
|
497
498
|
}
|
|
498
499
|
case "get_base_token_data": {
|
|
499
|
-
const parsed =
|
|
500
|
-
if (!parsed.
|
|
501
|
-
return
|
|
500
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(GetBaseTokenDataSchema, args);
|
|
501
|
+
if (!parsed.ok)
|
|
502
|
+
return parsed.error;
|
|
502
503
|
const address = parsed.data.tokenAddress.toLowerCase();
|
|
503
504
|
const [pair, coingeckoId] = await Promise.all([
|
|
504
505
|
fetchDexscreenerBaseToken(address),
|
package/dist/tools/memory.js
CHANGED
|
@@ -50,6 +50,7 @@ const public_url_js_1 = require("../public-url.js");
|
|
|
50
50
|
const local_vault_js_1 = require("../local-vault.js");
|
|
51
51
|
const local_memory_js_1 = require("../local-memory.js");
|
|
52
52
|
const _text_search_js_1 = require("../_text-search.js");
|
|
53
|
+
const _zod_helpers_js_1 = require("../_zod-helpers.js");
|
|
53
54
|
// memory_extract and memory_consolidate used to run their own LLM calls here
|
|
54
55
|
// (and a matching pair of Convex routes did the same server-side). Both are now
|
|
55
56
|
// two-pass: the tool fetches and stores, the caller decides what the facts are
|
|
@@ -192,6 +193,17 @@ async function syncToSupermemory(content, metadata, sourceUrl) {
|
|
|
192
193
|
console.error(`[memory] sync_failed: ${errMsg} | preview: "${preview}"`);
|
|
193
194
|
});
|
|
194
195
|
}
|
|
196
|
+
// A real auth failure must never be reported as "0 results found" - that
|
|
197
|
+
// reads as "your account genuinely has nothing saved" when the actual
|
|
198
|
+
// problem is the token/key was rejected. Only genuinely transient errors
|
|
199
|
+
// (network hiccup, the other RRF side still succeeding) degrade silently
|
|
200
|
+
// to an empty contribution - an auth failure is rethrown so it reaches the
|
|
201
|
+
// user instead of being swallowed. Found live: memory_search/memory_context/
|
|
202
|
+
// memory_insight/memory_consolidate all share this retrieval path, so a
|
|
203
|
+
// single fix here closes the same bug across all four tools.
|
|
204
|
+
function isAuthFailure(err) {
|
|
205
|
+
return err instanceof Error && /Authentication required/i.test(err.message);
|
|
206
|
+
}
|
|
195
207
|
async function searchSupermemory(query, limit = 10) {
|
|
196
208
|
try {
|
|
197
209
|
const local = (0, local_memory_js_1.getLocalMemoryConfig)();
|
|
@@ -200,7 +212,9 @@ async function searchSupermemory(query, limit = 10) {
|
|
|
200
212
|
const data = await (0, convex_js_1.callConvex)("/memory/search", "POST", { q: query, n: limit }, "memory_search");
|
|
201
213
|
return data?.results ?? [];
|
|
202
214
|
}
|
|
203
|
-
catch {
|
|
215
|
+
catch (err) {
|
|
216
|
+
if (isAuthFailure(err))
|
|
217
|
+
throw err;
|
|
204
218
|
return [];
|
|
205
219
|
}
|
|
206
220
|
}
|
|
@@ -218,7 +232,9 @@ async function lexicalSearch(query, limit = 30) {
|
|
|
218
232
|
rank: typeof r.rank === "number" ? r.rank : idx,
|
|
219
233
|
}));
|
|
220
234
|
}
|
|
221
|
-
catch {
|
|
235
|
+
catch (err) {
|
|
236
|
+
if (isAuthFailure(err))
|
|
237
|
+
throw err;
|
|
222
238
|
return [];
|
|
223
239
|
}
|
|
224
240
|
}
|
|
@@ -302,7 +318,7 @@ exports.MEMORY_TOOLS = [
|
|
|
302
318
|
"Unlike vault_save, memory_add is instant: no versioning, no type required. " +
|
|
303
319
|
"Use for notes, decisions, preferences, or anything you want to find later. " +
|
|
304
320
|
"Pass sourceUrl to fetch and index any web page, GitHub repo, or Notion page automatically - " +
|
|
305
|
-
"searchable in ~30s. Retrieval is
|
|
321
|
+
"searchable in ~30s. Retrieval is full-text (keyword) search, not embeddings - " +
|
|
306
322
|
"'what did I say about ETH yield?' finds notes containing those words or close variants, " +
|
|
307
323
|
"not unrelated phrasing with the same meaning. " +
|
|
308
324
|
"Auto-deduplicates: identical content in your recent 50 memories is skipped (override with force:true). " +
|
|
@@ -324,11 +340,11 @@ exports.MEMORY_TOOLS = [
|
|
|
324
340
|
},
|
|
325
341
|
{
|
|
326
342
|
name: "memory_search",
|
|
327
|
-
description: "
|
|
328
|
-
"
|
|
329
|
-
"
|
|
330
|
-
"
|
|
331
|
-
"
|
|
343
|
+
description: "Full-text (keyword) search over your stored memories, with 90-day time-decay weighting so " +
|
|
344
|
+
"recent notes outrank stale ones with similar wording. Good for exact-token lookups (env var " +
|
|
345
|
+
"names, contract addresses, IDs, specific phrases) - it does not understand meaning, so " +
|
|
346
|
+
"'low risk crypto yield' will not match a note phrased as 'conservative DeFi strategies' " +
|
|
347
|
+
"unless the words themselves overlap.",
|
|
332
348
|
inputSchema: {
|
|
333
349
|
type: "object",
|
|
334
350
|
properties: {
|
|
@@ -342,7 +358,7 @@ exports.MEMORY_TOOLS = [
|
|
|
342
358
|
name: "memory_context",
|
|
343
359
|
description: "Retrieve the most relevant memories for a topic, formatted as AI-ready context. " +
|
|
344
360
|
"Use at the start of research tasks to prime with everything stored about a topic. " +
|
|
345
|
-
"
|
|
361
|
+
"Uses full-text (keyword) search, not embeddings - phrase your topic with the words " +
|
|
346
362
|
"you expect were actually used when the memory was saved.",
|
|
347
363
|
inputSchema: {
|
|
348
364
|
type: "object",
|
|
@@ -522,9 +538,9 @@ function buildMemoryList(tag, results) {
|
|
|
522
538
|
async function handleMemoryTool(name, args) {
|
|
523
539
|
switch (name) {
|
|
524
540
|
case "memory_add": {
|
|
525
|
-
const parsed =
|
|
526
|
-
if (!parsed.
|
|
527
|
-
return
|
|
541
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(AddSchema, args);
|
|
542
|
+
if (!parsed.ok)
|
|
543
|
+
return parsed.error;
|
|
528
544
|
const { content, title, tags, sourceUrl, force } = parsed.data;
|
|
529
545
|
// `sourceUrl` is not stored, it is fetched and indexed — by the local
|
|
530
546
|
// memory server on the user's own machine when local mode is on. An
|
|
@@ -568,18 +584,9 @@ async function handleMemoryTool(name, args) {
|
|
|
568
584
|
// dedupes even before supermemory has indexed the first one.
|
|
569
585
|
if (!sourceUrl)
|
|
570
586
|
rememberRecentHash(hash, data?.id ?? "saved", title);
|
|
571
|
-
//
|
|
572
|
-
//
|
|
573
|
-
//
|
|
574
|
-
// autonomous contradiction call would break that. Instead this just
|
|
575
|
-
// resurfaces whatever memory_search's own retrieval already ranks as
|
|
576
|
-
// related to the content just saved, so the CALLING model - already
|
|
577
|
-
// reasoning about this exact save, in the same turn, no extra API
|
|
578
|
-
// cost - can judge for itself whether the two actually conflict and
|
|
579
|
-
// decide what to do (mark the old one superseded, fold both into
|
|
580
|
-
// memory_consolidate, or just note the new one is the current one).
|
|
581
|
-
// Best-effort: a search hiccup here must never fail or block the save
|
|
582
|
-
// that already succeeded above.
|
|
587
|
+
// Conflict hint, not a proof - no LLM call server-side (two-pass, no
|
|
588
|
+
// API key). Resurfaces related existing memories so the calling
|
|
589
|
+
// model decides. Best-effort, must never block the save above.
|
|
583
590
|
let conflictNote = "";
|
|
584
591
|
try {
|
|
585
592
|
const related = await hybridMemorySearch(content, 8);
|
|
@@ -623,9 +630,9 @@ async function handleMemoryTool(name, args) {
|
|
|
623
630
|
};
|
|
624
631
|
}
|
|
625
632
|
case "memory_search": {
|
|
626
|
-
const parsed =
|
|
627
|
-
if (!parsed.
|
|
628
|
-
return
|
|
633
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(SearchSchema, args);
|
|
634
|
+
if (!parsed.ok)
|
|
635
|
+
return parsed.error;
|
|
629
636
|
const { query, limit = 10 } = parsed.data;
|
|
630
637
|
// Over-fetch so post-decay ranking still has enough material.
|
|
631
638
|
const overfetch = Math.min(50, Math.max(limit * 2, 20));
|
|
@@ -705,9 +712,9 @@ async function handleMemoryTool(name, args) {
|
|
|
705
712
|
return { content: [{ type: "text", text: [header, "", ...rows, promotionHint].filter(Boolean).join("\n") }], structuredContent: buildMemorySearch(query, decayed) };
|
|
706
713
|
}
|
|
707
714
|
case "memory_context": {
|
|
708
|
-
const parsed =
|
|
709
|
-
if (!parsed.
|
|
710
|
-
return
|
|
715
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(ContextSchema, args);
|
|
716
|
+
if (!parsed.ok)
|
|
717
|
+
return parsed.error;
|
|
711
718
|
const { topic, limit = 8 } = parsed.data;
|
|
712
719
|
// v3.25.1: use hybrid retrieval so context loading picks up exact-token
|
|
713
720
|
// matches (env var names, model IDs, contract addresses) that semantic-
|
|
@@ -758,16 +765,16 @@ async function handleMemoryTool(name, args) {
|
|
|
758
765
|
`• memory_add (URL indexing) - ✅`,
|
|
759
766
|
`• Google Drive / Gmail / Notion - connect at finchagentic.com`,
|
|
760
767
|
``,
|
|
761
|
-
`**Capabilities:**
|
|
768
|
+
`**Capabilities:** Full-text (keyword) search with 90-day time-decay ranking - not embeddings, no vector search.`,
|
|
762
769
|
].join("\n"),
|
|
763
770
|
}],
|
|
764
771
|
structuredContent: buildMemoryProfile(data),
|
|
765
772
|
};
|
|
766
773
|
}
|
|
767
774
|
case "memory_list": {
|
|
768
|
-
const parsed =
|
|
769
|
-
if (!parsed.
|
|
770
|
-
return
|
|
775
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(ListSchema, args ?? {});
|
|
776
|
+
if (!parsed.ok)
|
|
777
|
+
return parsed.error;
|
|
771
778
|
const { limit = 20, tag } = parsed.data;
|
|
772
779
|
const localList = (0, local_memory_js_1.getLocalMemoryConfig)();
|
|
773
780
|
let results;
|
|
@@ -796,9 +803,9 @@ async function handleMemoryTool(name, args) {
|
|
|
796
803
|
return { content: [{ type: "text", text: [header, "", ...rows].join("\n") }], structuredContent: buildMemoryList(tag, results) };
|
|
797
804
|
}
|
|
798
805
|
case "memory_delete": {
|
|
799
|
-
const parsed =
|
|
800
|
-
if (!parsed.
|
|
801
|
-
return
|
|
806
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(DeleteMemSchema, args);
|
|
807
|
+
if (!parsed.ok)
|
|
808
|
+
return parsed.error;
|
|
802
809
|
if (args?.confirm !== true) {
|
|
803
810
|
return {
|
|
804
811
|
content: [{
|
|
@@ -827,9 +834,9 @@ async function handleMemoryTool(name, args) {
|
|
|
827
834
|
return { content: [{ type: "text", text: `🗑️ Memory deleted: \`${parsed.data.id}\`` }] };
|
|
828
835
|
}
|
|
829
836
|
case "memory_insight": {
|
|
830
|
-
const parsed =
|
|
831
|
-
if (!parsed.
|
|
832
|
-
return
|
|
837
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(InsightSchema, args);
|
|
838
|
+
if (!parsed.ok)
|
|
839
|
+
return parsed.error;
|
|
833
840
|
const { topic, depth = "standard" } = parsed.data;
|
|
834
841
|
const memLimit = depth === "deep" ? 15 : depth === "quick" ? 5 : 8;
|
|
835
842
|
// v3.25.1: hybrid retrieval for memory side (was semantic-only) so the
|
|
@@ -915,9 +922,9 @@ async function handleMemoryTool(name, args) {
|
|
|
915
922
|
return { content: [{ type: "text", text: lines.filter(l => l !== undefined).join("\n") }] };
|
|
916
923
|
}
|
|
917
924
|
case "memory_extract": {
|
|
918
|
-
const parsed =
|
|
919
|
-
if (!parsed.
|
|
920
|
-
return
|
|
925
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(ExtractSchema, args);
|
|
926
|
+
if (!parsed.ok)
|
|
927
|
+
return parsed.error;
|
|
921
928
|
const { text, facts, source = "extract" } = parsed.data;
|
|
922
929
|
// ── PASS 1: hand the text back with the extraction rubric ───────────
|
|
923
930
|
// Deciding what counts as a fact is judgement about *this user's*
|
|
@@ -972,9 +979,9 @@ async function handleMemoryTool(name, args) {
|
|
|
972
979
|
};
|
|
973
980
|
}
|
|
974
981
|
case "memory_consolidate": {
|
|
975
|
-
const parsed =
|
|
976
|
-
if (!parsed.
|
|
977
|
-
return
|
|
982
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(ConsolidateSchema, args);
|
|
983
|
+
if (!parsed.ok)
|
|
984
|
+
return parsed.error;
|
|
978
985
|
const { topic, limit = 12, summary } = parsed.data;
|
|
979
986
|
const localConsolidateCfg = (0, local_memory_js_1.getLocalMemoryConfig)();
|
|
980
987
|
// ── PASS 2: save the caller's merged version ────────────────────────
|
|
@@ -1037,22 +1044,10 @@ async function handleMemoryTool(name, args) {
|
|
|
1037
1044
|
}],
|
|
1038
1045
|
};
|
|
1039
1046
|
}
|
|
1040
|
-
// memory_publish
|
|
1041
|
-
//
|
|
1042
|
-
//
|
|
1043
|
-
//
|
|
1044
|
-
// app/convex/http.ts), so `published` was never actually set true; the
|
|
1045
|
-
// real publishEntry mutation (POST /vault/publish) existed but this tool
|
|
1046
|
-
// never called it. And even a correctly-wired publish would have done
|
|
1047
|
-
// nothing observable: nothing anywhere reads the `published` field for
|
|
1048
|
-
// cross-user browsing - no /vault/community route (dangling comment
|
|
1049
|
-
// only, same pattern as the other removed routes), no backend query for
|
|
1050
|
-
// it, no "Memory Marketplace" page in app/src. The tool asked users to
|
|
1051
|
-
// accept an "IRREVERSIBLE, PUBLIC" risk for a marketplace that doesn't
|
|
1052
|
-
// exist at any layer. Re-add only alongside building the actual
|
|
1053
|
-
// discovery path: a community-browse query + route + UI that reads
|
|
1054
|
-
// `published: true` entries. vault_unpublish is left in place - it's a
|
|
1055
|
-
// correct, harmless no-op until then.
|
|
1047
|
+
// memory_publish removed - broken (silently dropped isPublic/authorName,
|
|
1048
|
+
// never actually set `published`) AND pointless even if fixed - no
|
|
1049
|
+
// Memory Marketplace page/query ever reads that field. vault_unpublish
|
|
1050
|
+
// stays, it's a harmless no-op. Re-add only alongside building discovery.
|
|
1056
1051
|
default:
|
|
1057
1052
|
return null;
|
|
1058
1053
|
}
|
package/dist/tools/monitor.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.buildMonitorList = buildMonitorList;
|
|
|
5
5
|
exports.handleMonitorTool = handleMonitorTool;
|
|
6
6
|
const zod_1 = require("zod");
|
|
7
7
|
const convex_js_1 = require("../convex.js");
|
|
8
|
+
const _zod_helpers_js_1 = require("../_zod-helpers.js");
|
|
8
9
|
const TRIGGER_BASE = "https://api.trigger.dev/api/v1";
|
|
9
10
|
const MONITOR_TASK_ID = "finch-monitor";
|
|
10
11
|
const MONITOR_INPUT_SCHEMA = {
|
|
@@ -104,9 +105,9 @@ function buildMonitorList(schedules, configs = {}) {
|
|
|
104
105
|
}
|
|
105
106
|
async function handleMonitorTool(name, args) {
|
|
106
107
|
if (name === "schedule_research") {
|
|
107
|
-
const parsed =
|
|
108
|
-
if (!parsed.
|
|
109
|
-
return
|
|
108
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(CreateSchema, args);
|
|
109
|
+
if (!parsed.ok)
|
|
110
|
+
return parsed.error;
|
|
110
111
|
const key = getKey();
|
|
111
112
|
if (!key)
|
|
112
113
|
return noKeyMsg();
|
|
@@ -282,9 +283,9 @@ async function handleMonitorTool(name, args) {
|
|
|
282
283
|
}
|
|
283
284
|
}
|
|
284
285
|
if (name === "cancel_monitor") {
|
|
285
|
-
const parsed =
|
|
286
|
-
if (!parsed.
|
|
287
|
-
return
|
|
286
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(CancelSchema, args);
|
|
287
|
+
if (!parsed.ok)
|
|
288
|
+
return parsed.error;
|
|
288
289
|
if (args?.confirm !== true) {
|
|
289
290
|
return {
|
|
290
291
|
content: [{
|
package/dist/tools/os.js
CHANGED
|
@@ -2,20 +2,17 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OS_TOOLS = void 0;
|
|
4
4
|
exports.handleOsTool = handleOsTool;
|
|
5
|
-
const convex_js_1 = require("../convex.js");
|
|
6
|
-
const token_gate_js_1 = require("../token-gate.js");
|
|
7
|
-
const wallet_js_1 = require("../wallet.js");
|
|
8
5
|
const local_memory_js_1 = require("../local-memory.js");
|
|
9
6
|
const config_js_1 = require("../config.js");
|
|
10
7
|
const CONVEX_SITE = process.env.FINCH_CONVEX_URL ?? "https://valuable-fish-533.convex.site";
|
|
8
|
+
// finch_status moved to ../finch-status.ts (Sep 2026) - the old version here
|
|
9
|
+
// made 6 Promise.allSettled backend calls and silently defaulted every field
|
|
10
|
+
// to zero/basic on ANY failure (including a 401), which read as "empty but
|
|
11
|
+
// healthy" instead of surfacing a real auth problem. The new one makes one
|
|
12
|
+
// cheap authed call and reports the real state. Kept out of OS_TOOLS/this
|
|
13
|
+
// switch entirely (not just filtered at list time) so there's exactly one
|
|
14
|
+
// place this tool is defined - server.ts wires it in directly.
|
|
11
15
|
exports.OS_TOOLS = [
|
|
12
|
-
{
|
|
13
|
-
name: "finch_status",
|
|
14
|
-
description: "Full runtime dashboard - memory size, persistent agents, active automations, recent vault research, " +
|
|
15
|
-
"execution scores, and your tier. Like `htop` for your AI runtime. " +
|
|
16
|
-
"Run this to see what's running and what state your runtime is currently holding.",
|
|
17
|
-
inputSchema: { type: "object", properties: {}, required: [] },
|
|
18
|
-
},
|
|
19
16
|
{
|
|
20
17
|
name: "finch_diagnostics",
|
|
21
18
|
description: "Health check for all Finch services - Convex backend, Firecrawl, Supermemory, and configured API keys. " +
|
|
@@ -38,81 +35,6 @@ exports.OS_TOOLS = [
|
|
|
38
35
|
];
|
|
39
36
|
async function handleOsTool(name, args) {
|
|
40
37
|
switch (name) {
|
|
41
|
-
case "finch_status": {
|
|
42
|
-
// Respect local memory mode here too - querying Convex's /memory/profile
|
|
43
|
-
// when writes have been going to a local supermemory server instead
|
|
44
|
-
// would report a stale/zero count (the exact bug memory_profile's own
|
|
45
|
-
// handler was fixed to avoid).
|
|
46
|
-
const localStatusCfg = (0, local_memory_js_1.getLocalMemoryConfig)();
|
|
47
|
-
const memoryProfileCall = localStatusCfg
|
|
48
|
-
? (0, local_memory_js_1.localMemoryProfile)(localStatusCfg)
|
|
49
|
-
: (0, convex_js_1.callConvex)("/memory/profile", "GET", undefined, "memory_profile");
|
|
50
|
-
const [tierResult, walletResult, memRes, autoRes, vaultRes, agentsRes] = await Promise.allSettled([
|
|
51
|
-
(0, token_gate_js_1.getTier)(),
|
|
52
|
-
(0, wallet_js_1.getOrCreateWallet)(),
|
|
53
|
-
memoryProfileCall,
|
|
54
|
-
(0, convex_js_1.callConvex)("/automations/list", "GET", undefined, "list_automations"),
|
|
55
|
-
(0, convex_js_1.callConvex)("/vault/list?type=research&limit=5", "GET", undefined, "vault_list"),
|
|
56
|
-
(0, convex_js_1.callConvex)("/vault/list?type=memory&limit=20", "GET", undefined, "vault_list"),
|
|
57
|
-
]);
|
|
58
|
-
const tier = tierResult.status === "fulfilled" ? tierResult.value : "basic";
|
|
59
|
-
const wallet = walletResult.status === "fulfilled" ? walletResult.value : null;
|
|
60
|
-
const mem = memRes.status === "fulfilled" ? memRes.value : null;
|
|
61
|
-
const autos = autoRes.status === "fulfilled" ? autoRes.value : null;
|
|
62
|
-
const vault = vaultRes.status === "fulfilled" ? vaultRes.value : null;
|
|
63
|
-
const agents = agentsRes.status === "fulfilled" ? agentsRes.value : null;
|
|
64
|
-
const automations = autos?.automations ?? [];
|
|
65
|
-
const activeAutos = automations.filter((a) => a.status === "active");
|
|
66
|
-
const vaultEntries = vault?.entries ?? [];
|
|
67
|
-
// Persistent agents live in vault as type=memory with key prefix "agent/"
|
|
68
|
-
const persistentAgents = (agents?.entries ?? []).filter((e) => typeof e.key === "string" && e.key.startsWith("agent/"));
|
|
69
|
-
const memTotal = mem?.total ?? 0;
|
|
70
|
-
const memStatus = mem?.status ?? "unknown";
|
|
71
|
-
const tierLabel = tier === "holder"
|
|
72
|
-
? "\u{1F7E2} **Holder** - premium tools unlocked"
|
|
73
|
-
: "⚪ **Basic** - hold FINCH on Base to unlock premium tools";
|
|
74
|
-
const walletShort = wallet
|
|
75
|
-
? `${wallet.address.slice(0, 6)}...${wallet.address.slice(-4)}`
|
|
76
|
-
: "not configured";
|
|
77
|
-
const lines = [
|
|
78
|
-
`**Finch Runtime - System Status**`,
|
|
79
|
-
`────────────────────────────────`,
|
|
80
|
-
``,
|
|
81
|
-
`🔑 **Tier** ${tierLabel}`,
|
|
82
|
-
`👛 **Wallet** ${walletShort}`,
|
|
83
|
-
``,
|
|
84
|
-
`🧠 **Memory** ${memStatus === "ok" ? "✅" : "⚠️"} ${memTotal} entries · Space: ${mem?.space ?? "-"}`,
|
|
85
|
-
`🤖 **Agents** ${persistentAgents.length} persistent agent${persistentAgents.length === 1 ? "" : "s"} in vault`,
|
|
86
|
-
`⚡ **Automations** ${activeAutos.length} active of ${automations.length} total`,
|
|
87
|
-
`📚 **Vault** ${vaultEntries.length} recent research entries`,
|
|
88
|
-
``,
|
|
89
|
-
];
|
|
90
|
-
if (activeAutos.length > 0) {
|
|
91
|
-
lines.push(`**Active Automations:**`);
|
|
92
|
-
for (const a of activeAutos.slice(0, 5)) {
|
|
93
|
-
const next = a.nextRunAt ? ` · next ${new Date(a.nextRunAt).toUTCString()}` : "";
|
|
94
|
-
lines.push(` • ${a.name} - ${a.triggerType}${next}`);
|
|
95
|
-
}
|
|
96
|
-
lines.push("");
|
|
97
|
-
}
|
|
98
|
-
if (persistentAgents.length > 0) {
|
|
99
|
-
lines.push(`**Persistent Agents:**`);
|
|
100
|
-
for (const a of persistentAgents.slice(0, 5)) {
|
|
101
|
-
const name = a.key.replace(/^agent\//, "");
|
|
102
|
-
lines.push(` • ${name} - v${a.version ?? 1}`);
|
|
103
|
-
}
|
|
104
|
-
lines.push("");
|
|
105
|
-
}
|
|
106
|
-
if (vaultEntries.length > 0) {
|
|
107
|
-
lines.push(`**Recent Research:**`);
|
|
108
|
-
for (const e of vaultEntries) {
|
|
109
|
-
lines.push(` • [${e.agentId ?? "vault"}] ${e.title}`);
|
|
110
|
-
}
|
|
111
|
-
lines.push("");
|
|
112
|
-
}
|
|
113
|
-
lines.push(`💡 Run \`deep_research query: "..."\` to launch multi-agent research · \`agent_spawn\` to start a persistent agent`);
|
|
114
|
-
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
115
|
-
}
|
|
116
38
|
case "finch_diagnostics": {
|
|
117
39
|
const CONVEX_URL = process.env.FINCH_CONVEX_URL ?? "https://valuable-fish-533.convex.site";
|
|
118
40
|
// Ping root of each service — any non-5xx means the host is up
|
package/dist/tools/research.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.handleResearchTool = handleResearchTool;
|
|
|
5
5
|
const zod_1 = require("zod");
|
|
6
6
|
const convex_js_1 = require("../convex.js");
|
|
7
7
|
const public_url_js_1 = require("../public-url.js");
|
|
8
|
+
const _zod_helpers_js_1 = require("../_zod-helpers.js");
|
|
8
9
|
const FC_BASE = "https://api.firecrawl.dev/v1";
|
|
9
10
|
exports.RESEARCH_TOOLS = [
|
|
10
11
|
{
|
|
@@ -98,9 +99,9 @@ async function basicFetch(url) {
|
|
|
98
99
|
}
|
|
99
100
|
async function handleResearchTool(name, args) {
|
|
100
101
|
if (name === "web_scrape") {
|
|
101
|
-
const parsed =
|
|
102
|
-
if (!parsed.
|
|
103
|
-
return
|
|
102
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(ScrapeSchema, args);
|
|
103
|
+
if (!parsed.ok)
|
|
104
|
+
return parsed.error;
|
|
104
105
|
const { url, focus } = parsed.data;
|
|
105
106
|
const unsafe = await (0, public_url_js_1.assertPublicUrl)(url);
|
|
106
107
|
if (unsafe) {
|
|
@@ -118,9 +119,9 @@ async function handleResearchTool(name, args) {
|
|
|
118
119
|
return { content: [{ type: "text", text: `**${url}**${focusNote}${body}\n\n_Source: ${source}_` }] };
|
|
119
120
|
}
|
|
120
121
|
if (name === "web_search") {
|
|
121
|
-
const parsed =
|
|
122
|
-
if (!parsed.
|
|
123
|
-
return
|
|
122
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(SearchSchema, args);
|
|
123
|
+
if (!parsed.ok)
|
|
124
|
+
return parsed.error;
|
|
124
125
|
const { query, limit = 5 } = parsed.data;
|
|
125
126
|
// Priority 1: user BYOK direct path. Priority 2: Finch proxy.
|
|
126
127
|
// Same data shape so the formatting code below is identical for both.
|