@finchagentic/mcp 4.2.0 → 4.5.0
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 +17 -12
- package/dist/annotations.js +9 -1
- package/dist/cli.js +139 -0
- package/dist/convex.js +31 -4
- package/dist/index.js +11 -8
- package/dist/project.js +36 -0
- package/dist/server.js +14 -6
- package/dist/tool-filter.js +5 -5
- package/dist/tools/agents.js +46 -6
- package/dist/tools/automation.js +47 -7
- package/dist/tools/coder.js +1 -1
- package/dist/tools/deep-research.js +1 -1
- package/dist/tools/defi.js +4 -4
- package/dist/tools/equity.js +1 -1
- package/dist/tools/events.js +1 -1
- package/dist/tools/github.js +51 -1
- package/dist/tools/insider.js +1 -1
- package/dist/tools/insight.js +2 -2
- package/dist/tools/market.js +5 -5
- package/dist/tools/memory.js +8 -8
- package/dist/tools/miroshark.js +8 -1
- package/dist/tools/monitor.js +2 -2
- package/dist/tools/os.js +9 -3
- package/dist/tools/research-chain.js +1 -1
- package/dist/tools/research-compare.js +1 -1
- package/dist/tools/research.js +2 -2
- package/dist/tools/rh-bridge.js +1 -1
- package/dist/tools/rh-mcp.js +16 -2
- package/dist/tools/rh-orders.js +13 -6
- package/dist/tools/scanner.js +3 -3
- package/dist/tools/stake.js +43 -3
- package/dist/tools/vault.js +195 -19
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -29,13 +29,13 @@ Always pin the version. Never use `@latest`.
|
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
31
|
# One-command installer (detects common MCP clients)
|
|
32
|
-
npx -y -p @finchagentic/mcp@4.
|
|
32
|
+
npx -y -p @finchagentic/mcp@4.5.0 finch install
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
### Claude Code
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
claude mcp add finch -s user -- npx -y -p @finchagentic/mcp@4.
|
|
38
|
+
claude mcp add finch -s user -- npx -y -p @finchagentic/mcp@4.5.0 finch-mcp
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
### Cursor / Windsurf / Claude Desktop
|
|
@@ -45,7 +45,7 @@ claude mcp add finch -s user -- npx -y -p @finchagentic/mcp@4.2.0 finch-mcp
|
|
|
45
45
|
"mcpServers": {
|
|
46
46
|
"finch": {
|
|
47
47
|
"command": "npx",
|
|
48
|
-
"args": ["-y", "-p", "@finchagentic/mcp@4.
|
|
48
|
+
"args": ["-y", "-p", "@finchagentic/mcp@4.5.0", "finch-mcp"]
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
}
|
|
@@ -59,7 +59,7 @@ claude mcp add finch -s user -- npx -y -p @finchagentic/mcp@4.2.0 finch-mcp
|
|
|
59
59
|
"finch": {
|
|
60
60
|
"type": "stdio",
|
|
61
61
|
"command": "npx",
|
|
62
|
-
"args": ["-y", "-p", "@finchagentic/mcp@4.
|
|
62
|
+
"args": ["-y", "-p", "@finchagentic/mcp@4.5.0", "finch-mcp"]
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -79,7 +79,7 @@ claude mcp add finch -s user -- npx -y -p @finchagentic/mcp@4.2.0 finch-mcp
|
|
|
79
79
|
|
|
80
80
|
</details>
|
|
81
81
|
|
|
82
|
-
No LLM API key is required to start —
|
|
82
|
+
No LLM API key is required to start — 111 of 116 tools are plain reads/writes/on-chain calls that your MCP client's own model already drives; only 5 (`ask_finch`, `deep_research`, and scheduled agent learning) do their own multi-step reasoning server-side and need a key (see [Configuration](#configuration)). Tools load on first use.
|
|
83
83
|
|
|
84
84
|
## Quick start
|
|
85
85
|
|
|
@@ -87,6 +87,7 @@ No LLM API key is required to start — 108 of 113 tools are plain reads/writes/
|
|
|
87
87
|
finch doctor # health check
|
|
88
88
|
finch setup # local vault / memory / providers
|
|
89
89
|
finch vault # inspect local vault
|
|
90
|
+
finch orders # schedule Robinhood Chain DCA / TP-SL order ticks
|
|
90
91
|
```
|
|
91
92
|
|
|
92
93
|
Try in your MCP client:
|
|
@@ -99,15 +100,19 @@ save this thesis to vault
|
|
|
99
100
|
|
|
100
101
|
## What you get
|
|
101
102
|
|
|
102
|
-
**
|
|
103
|
+
**116 tools** across four pillars:
|
|
103
104
|
|
|
104
105
|
| Pillar | What it does |
|
|
105
106
|
|--------|----------------|
|
|
106
107
|
| **Memory** | Full-text searchable memory + versioned vault + chronicle |
|
|
107
|
-
| **Agents** | Spawn, recall, update named agents |
|
|
108
|
-
| **Workflows** | Automations, monitors, packets, deep research |
|
|
108
|
+
| **Agents** | Spawn, recall, update named agents — `agent_recall` also pulls related memory/vault context matching the agent's goal, not just its own logged updates |
|
|
109
|
+
| **Workflows** | Automations, monitors, packets, deep research (auto-saves reports + auto-links related past research) |
|
|
109
110
|
| **Execution** | Base DeFi, Robinhood Chain, market data, web, GitHub |
|
|
110
111
|
|
|
112
|
+
Coding and research sessions persist the same way: `deep_research` auto-saves its report to vault and links it to related past reports; `code_session_save` does the same for coding/debugging sessions (`vault_save type=code`, versioned per project, auto-linked). Both exist so the next session — yours or another agent's — starts with real context instead of cold.
|
|
113
|
+
|
|
114
|
+
`vault_save` and `agent_spawn` also take an optional `workspaceProject` - the same named Projects a user organizes their Agents/vault content into on the webapp's Agents page. Pass a name and it's matched case-insensitively or created automatically (`list_projects` to browse what exists first). Hosted vault only - local-vault mode has no project concept.
|
|
115
|
+
|
|
111
116
|
Default palette is `core` (lighter context). Full set:
|
|
112
117
|
|
|
113
118
|
```json
|
|
@@ -119,7 +124,7 @@ Default palette is `core` (lighter context). Full set:
|
|
|
119
124
|
Finch is the runtime. **Your LLM is the brain. Your data stays yours.**
|
|
120
125
|
|
|
121
126
|
```bash
|
|
122
|
-
npx -y -p @finchagentic/mcp@4.
|
|
127
|
+
npx -y -p @finchagentic/mcp@4.5.0 finch setup
|
|
123
128
|
# enable local vault (and optional local memory)
|
|
124
129
|
```
|
|
125
130
|
|
|
@@ -146,12 +151,12 @@ Scheduled/cloud features still need an account. Core memory, vault, and public-d
|
|
|
146
151
|
| `GITHUB_TOKEN` | For `github_search_code` |
|
|
147
152
|
| `ALCHEMY_API_KEY` | Faster Base RPC (optional) |
|
|
148
153
|
|
|
149
|
-
**Cost model:** almost everything is free to run — the other
|
|
154
|
+
**Cost model:** almost everything is free to run — the other 110 tools are plain API/RPC calls, and your MCP client's own model (Claude, GPT, whatever's driving the chat) does all the tool-selection reasoning at no cost to Finch. The 5 exceptions above need their own key because their reasoning happens *inside* the tool call, invisible to your client, and can't be delegated to it. Set exactly one of the four env vars and every tool that needs it will use it automatically.
|
|
150
155
|
|
|
151
156
|
Guided setup:
|
|
152
157
|
|
|
153
158
|
```bash
|
|
154
|
-
npx -y -p @finchagentic/mcp@4.
|
|
159
|
+
npx -y -p @finchagentic/mcp@4.5.0 finch setup
|
|
155
160
|
```
|
|
156
161
|
|
|
157
162
|
## Security
|
|
@@ -160,7 +165,7 @@ npx -y -p @finchagentic/mcp@4.2.0 finch setup
|
|
|
160
165
|
|:-:|----------|------|
|
|
161
166
|
| 1 | Prompt injection | External content is data only — never instructions |
|
|
162
167
|
| 2 | Mainnet confirm | Estimate → preview → confirm → execute |
|
|
163
|
-
| 3 | Pinned install | Always `@finchagentic/mcp@4.
|
|
168
|
+
| 3 | Pinned install | Always `@finchagentic/mcp@4.5.0`, never `@latest` |
|
|
164
169
|
| 4 | Credential vault | Never paste secrets into prompts or third-party tools |
|
|
165
170
|
| 5 | Data disclosure | Know what leaves the machine (LLM, Firecrawl, GitHub, chain RPCs) |
|
|
166
171
|
| 6 | Server monitors | Scheduled jobs need explicit confirmation |
|
package/dist/annotations.js
CHANGED
|
@@ -26,7 +26,7 @@ const WRITE_IDEMPOTENT = new Set([
|
|
|
26
26
|
const WRITE = new Set([
|
|
27
27
|
"agent_spawn",
|
|
28
28
|
"chronicle_add",
|
|
29
|
-
"
|
|
29
|
+
"code_session_save",
|
|
30
30
|
"memory_add",
|
|
31
31
|
"memory_extract",
|
|
32
32
|
"memory_consolidate",
|
|
@@ -65,6 +65,13 @@ const DESTRUCTIVE = new Set([
|
|
|
65
65
|
"rh_mcp_swap",
|
|
66
66
|
"rh_dca_create", // arms recurring real buys
|
|
67
67
|
"rh_bracket_create", // arms real TP/SL sells
|
|
68
|
+
// a swap/send automation arms unattended, REPEATING real fund movement
|
|
69
|
+
// (the backend's 1-minute cron evaluator fires it) - same risk class as
|
|
70
|
+
// rh_dca_create/rh_bracket_create above, not a plain additive write. An
|
|
71
|
+
// alert-only automation doesn't move funds, but the tool can't tell which
|
|
72
|
+
// kind it's about to create until AFTER the backend parses rawInput, so
|
|
73
|
+
// it's classified by its worst case, same reasoning as rh_orders_tick.
|
|
74
|
+
"create_automation",
|
|
68
75
|
"rh_orders_tick", // preview by default, but can execute:true and move funds
|
|
69
76
|
// executors that run other (possibly fund-moving) tools
|
|
70
77
|
"run_automation",
|
|
@@ -73,6 +80,7 @@ const DESTRUCTIVE = new Set([
|
|
|
73
80
|
// for a fixed period; unstake moves it (plus rewards) back
|
|
74
81
|
"stake_finch",
|
|
75
82
|
"unstake_finch",
|
|
83
|
+
"claim_vested_rewards", // treasury -> custodial wallet USDG transfer
|
|
76
84
|
]);
|
|
77
85
|
function annotationsFor(name) {
|
|
78
86
|
if (DESTRUCTIVE.has(name)) {
|
package/dist/cli.js
CHANGED
|
@@ -1127,6 +1127,138 @@ function vaultFlow() {
|
|
|
1127
1127
|
console.log(` ${C.cyan}git -C ${shortDir} init${C.reset} ${C.dim}# version-control it${C.reset}`);
|
|
1128
1128
|
console.log(` ${C.dim}Nothing leaves this machine unless you move it.${C.reset}\n`);
|
|
1129
1129
|
}
|
|
1130
|
+
// ── Robinhood Chain order scheduler ──────────────────────────────────────────
|
|
1131
|
+
//
|
|
1132
|
+
// rh_dca_create/rh_bracket_create (src/tools/rh-orders.ts) only ever write a
|
|
1133
|
+
// LOCAL pending order - nothing executes it. Only rh_orders_tick does, and
|
|
1134
|
+
// per that file's own header comment, "an always-on scheduler must call
|
|
1135
|
+
// rh_orders_tick {execute:true} periodically" - which this package never
|
|
1136
|
+
// actually set up. A user who creates a daily DCA and closes their laptop
|
|
1137
|
+
// got nothing, silently: no error, no warning, the order just sat there.
|
|
1138
|
+
// These three subcommands are that missing scheduler, reusing the existing
|
|
1139
|
+
// tick/lock/kill-switch logic as-is - "wire up the missing piece", not
|
|
1140
|
+
// "build order execution from scratch".
|
|
1141
|
+
async function runOrdersTick(execute) {
|
|
1142
|
+
const { handleRhOrderTool } = await Promise.resolve().then(() => __importStar(require("./tools/rh-orders.js")));
|
|
1143
|
+
const result = await handleRhOrderTool("rh_orders_tick", { execute });
|
|
1144
|
+
const text = result?.content?.[0]?.type === "text" ? result.content[0].text : "(no output)";
|
|
1145
|
+
return { text, isError: !!result?.isError };
|
|
1146
|
+
}
|
|
1147
|
+
function installOrdersScheduler() {
|
|
1148
|
+
// Version-pinned npx invocation, not a hardcoded path to this process's
|
|
1149
|
+
// own binary - matches this package's own "always pin the version, never
|
|
1150
|
+
// @latest" security guidance (README), and survives regardless of whether
|
|
1151
|
+
// the user got here via a global install or a one-off `npx -p`, whose
|
|
1152
|
+
// resolved path may not exist by the time the scheduler fires later.
|
|
1153
|
+
const invocation = `npx -y -p @finchagentic/mcp@${PKG_VERSION} finch orders tick --execute`;
|
|
1154
|
+
if (process.platform === "win32") {
|
|
1155
|
+
const taskName = "FinchOrdersTick";
|
|
1156
|
+
try {
|
|
1157
|
+
child_process.execSync(`schtasks /Create /TN "${taskName}" /TR "${invocation}" /SC MINUTE /MO 15 /F`, { stdio: "pipe" });
|
|
1158
|
+
console.log(`\n ${C.green}✓${C.reset} Registered Windows Task Scheduler task "${taskName}" - ticks every 15 min, survives reboots.`);
|
|
1159
|
+
console.log(` ${C.dim}Remove it:${C.reset} schtasks /Delete /TN "${taskName}" /F`);
|
|
1160
|
+
}
|
|
1161
|
+
catch (err) {
|
|
1162
|
+
console.error(` ${C.red}✗ schtasks failed: ${err.message}${C.reset}`);
|
|
1163
|
+
console.error(` ${C.dim}Try running as Administrator, or use \`finch orders daemon\` instead.${C.reset}\n`);
|
|
1164
|
+
process.exit(1);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
else {
|
|
1168
|
+
const logFile = path.join(os.homedir(), ".finch", "orders-tick.log");
|
|
1169
|
+
const line = `*/15 * * * * ${invocation} >> ${logFile} 2>&1`;
|
|
1170
|
+
try {
|
|
1171
|
+
const existing = (() => {
|
|
1172
|
+
try {
|
|
1173
|
+
return child_process.execSync("crontab -l", { stdio: ["pipe", "pipe", "ignore"] }).toString();
|
|
1174
|
+
}
|
|
1175
|
+
catch {
|
|
1176
|
+
return "";
|
|
1177
|
+
} // no crontab yet for this user - not an error
|
|
1178
|
+
})();
|
|
1179
|
+
if (existing.includes("finch orders tick")) {
|
|
1180
|
+
console.log(`\n ${C.yellow}○${C.reset} A finch orders tick entry already exists in crontab - not adding a duplicate.\n`);
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
const next = existing.trim().length > 0 ? `${existing.trim()}\n${line}\n` : `${line}\n`;
|
|
1184
|
+
child_process.execSync("crontab -", { input: next });
|
|
1185
|
+
console.log(`\n ${C.green}✓${C.reset} Added a crontab entry - ticks every 15 min, survives reboots.`);
|
|
1186
|
+
console.log(` ${C.dim}Remove it:${C.reset} crontab -e ${C.dim}(delete the finch orders line)${C.reset}`);
|
|
1187
|
+
console.log(` ${C.dim}Logs:${C.reset} ${logFile}`);
|
|
1188
|
+
}
|
|
1189
|
+
catch (err) {
|
|
1190
|
+
console.error(` ${C.red}✗ crontab failed: ${err.message}${C.reset}`);
|
|
1191
|
+
console.error(` ${C.dim}Try \`finch orders daemon\` instead.${C.reset}\n`);
|
|
1192
|
+
process.exit(1);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
console.log(` ${C.dim}Only the local wallet's own default key is needed for signing - set FINCH_WALLET_PASSPHRASE as a persistent system env var (not just your shell rc file) if you use one, since scheduled tasks don't inherit an interactive shell's environment.${C.reset}\n`);
|
|
1196
|
+
}
|
|
1197
|
+
async function ordersFlow() {
|
|
1198
|
+
const sub = process.argv[3];
|
|
1199
|
+
if (sub === "tick") {
|
|
1200
|
+
const execute = process.argv.includes("--execute");
|
|
1201
|
+
const { text, isError } = await runOrdersTick(execute);
|
|
1202
|
+
console.log(`\n${text}\n`);
|
|
1203
|
+
if (isError)
|
|
1204
|
+
process.exit(1);
|
|
1205
|
+
return;
|
|
1206
|
+
}
|
|
1207
|
+
if (sub === "daemon") {
|
|
1208
|
+
const intervalFlag = process.argv.indexOf("--interval");
|
|
1209
|
+
const intervalMinutes = intervalFlag !== -1 ? Number(process.argv[intervalFlag + 1]) : 15;
|
|
1210
|
+
if (!(intervalMinutes > 0)) {
|
|
1211
|
+
console.error(` ${C.red}✗ --interval must be a positive number of minutes${C.reset}\n`);
|
|
1212
|
+
process.exit(1);
|
|
1213
|
+
}
|
|
1214
|
+
console.log(`\n ${C.cyan}${C.bold}finch orders daemon${C.reset} ${C.dim}- ticking every ${intervalMinutes}m, Ctrl+C to stop${C.reset}`);
|
|
1215
|
+
console.log(` ${C.dim}Kill-switch: RH_ORDERS_DISABLED=1 or create ~/.finch/rh-orders.OFF${C.reset}`);
|
|
1216
|
+
console.log(` ${C.dim}Note: this only runs while this process stays alive - for something that${C.reset}`);
|
|
1217
|
+
console.log(` ${C.dim}survives reboots/terminal closure, use \`finch orders install-scheduler\` instead.${C.reset}\n`);
|
|
1218
|
+
let running = false; // skip a tick if the previous one is still in flight (slow RPC), don't queue up
|
|
1219
|
+
const tick = async () => {
|
|
1220
|
+
if (running)
|
|
1221
|
+
return;
|
|
1222
|
+
running = true;
|
|
1223
|
+
const ts = new Date().toISOString();
|
|
1224
|
+
try {
|
|
1225
|
+
const { text } = await runOrdersTick(true);
|
|
1226
|
+
console.log(` ${C.dim}[${ts}]${C.reset}`);
|
|
1227
|
+
console.log(text.split("\n").map((l) => ` ${l}`).join("\n") + "\n");
|
|
1228
|
+
}
|
|
1229
|
+
catch (err) {
|
|
1230
|
+
console.error(` ${C.red}[${ts}] tick error: ${err.message ?? err}${C.reset}\n`);
|
|
1231
|
+
}
|
|
1232
|
+
finally {
|
|
1233
|
+
running = false;
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1236
|
+
await tick();
|
|
1237
|
+
const timer = setInterval(tick, intervalMinutes * 60000);
|
|
1238
|
+
const shutdown = () => {
|
|
1239
|
+
clearInterval(timer);
|
|
1240
|
+
console.log(`\n ${C.dim}Stopped.${C.reset}\n`);
|
|
1241
|
+
process.exit(0);
|
|
1242
|
+
};
|
|
1243
|
+
process.on("SIGINT", shutdown);
|
|
1244
|
+
process.on("SIGTERM", shutdown);
|
|
1245
|
+
return; // process stays alive via the interval timer
|
|
1246
|
+
}
|
|
1247
|
+
if (sub === "install-scheduler") {
|
|
1248
|
+
installOrdersScheduler();
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
1251
|
+
console.log(`
|
|
1252
|
+
${C.cyan}${C.bold}finch orders${C.reset} ${C.dim}- Robinhood Chain DCA/TP-SL orders (rh_dca_create/rh_bracket_create)${C.reset}
|
|
1253
|
+
${C.dim}do nothing on their own until one of these is running - creating an order${C.reset}
|
|
1254
|
+
${C.dim}only ever writes it locally, nothing executes it automatically by itself.${C.reset}
|
|
1255
|
+
|
|
1256
|
+
${C.cyan}Commands:${C.reset}
|
|
1257
|
+
finch orders tick [--execute] Run one tick now and exit (preview by default)
|
|
1258
|
+
finch orders daemon [--interval N] Run in the foreground, ticking every N min (default 15)
|
|
1259
|
+
finch orders install-scheduler Register an OS-level recurring task (Task Scheduler / cron)
|
|
1260
|
+
`);
|
|
1261
|
+
}
|
|
1130
1262
|
// ── Entry point ───────────────────────────────────────────────────────────────
|
|
1131
1263
|
const cmd = process.argv[2];
|
|
1132
1264
|
if (cmd === "install") {
|
|
@@ -1158,6 +1290,12 @@ else if (cmd === "setup") {
|
|
|
1158
1290
|
else if (cmd === "vault") {
|
|
1159
1291
|
vaultFlow();
|
|
1160
1292
|
}
|
|
1293
|
+
else if (cmd === "orders") {
|
|
1294
|
+
ordersFlow().catch((err) => {
|
|
1295
|
+
console.error(` ${C.red}✗ orders error: ${err.message}${C.reset}`);
|
|
1296
|
+
process.exit(1);
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1161
1299
|
else if (cmd === "logout") {
|
|
1162
1300
|
const cfg = (0, config_js_1.readConfig)();
|
|
1163
1301
|
if (!cfg.sessionToken) {
|
|
@@ -1239,6 +1377,7 @@ else if (cmd === "help" || cmd === "--help" || cmd === "-h") {
|
|
|
1239
1377
|
finch doctor Run a full health check + suggest fixes
|
|
1240
1378
|
finch setup Configure your own LLM key and/or local memory + vault
|
|
1241
1379
|
finch vault Show your local vault (location, contents, backup)
|
|
1380
|
+
finch orders Run/schedule Robinhood Chain DCA & TP-SL order ticks
|
|
1242
1381
|
finch help Show this help
|
|
1243
1382
|
|
|
1244
1383
|
${C.dim}Claude Code / Cursor / Windsurf / Codex / Aeon / Antigravity / Zed — anywhere MCP runs.${C.reset}
|
package/dist/convex.js
CHANGED
|
@@ -28,7 +28,17 @@ async function attemptConvex(url, method, headers, body, timeoutMs = 30000) {
|
|
|
28
28
|
signal: AbortSignal.timeout(timeoutMs),
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
|
-
async function callConvex(path, method, body, toolName = "unknown", timeoutMs = 30000
|
|
31
|
+
async function callConvex(path, method, body, toolName = "unknown", timeoutMs = 30000,
|
|
32
|
+
// Fund-moving mutations (stake, unstake, claim rewards, a real - non-
|
|
33
|
+
// dryRun - automation trigger) are NOT idempotent server-side: there is
|
|
34
|
+
// no request-id dedup on the backend, so silently retrying a POST whose
|
|
35
|
+
// response was merely lost (a network blip or client-side timeout AFTER
|
|
36
|
+
// the server already processed it) can double-execute a real transfer.
|
|
37
|
+
// Pass true here for any such call - it trades the convenience of an
|
|
38
|
+
// automatic retry for never risking a duplicate execution; the caller
|
|
39
|
+
// sees a clear "may have already gone through" error instead and can
|
|
40
|
+
// check status before deciding to retry by hand.
|
|
41
|
+
noRetry = false) {
|
|
32
42
|
const url = `${exports.CONVEX_SITE}${path}`;
|
|
33
43
|
const headers = { "Content-Type": "application/json" };
|
|
34
44
|
const apiKey = process.env.FINCH_API_KEY;
|
|
@@ -66,7 +76,8 @@ async function callConvex(path, method, body, toolName = "unknown", timeoutMs =
|
|
|
66
76
|
if (process.env.BANKR_API_KEY)
|
|
67
77
|
headers["X-User-Bankr-Key"] = process.env.BANKR_API_KEY;
|
|
68
78
|
let lastError = null;
|
|
69
|
-
|
|
79
|
+
const attempts = noRetry ? 1 : RETRY_DELAYS.length;
|
|
80
|
+
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
70
81
|
if (attempt > 0) {
|
|
71
82
|
await new Promise((r) => setTimeout(r, RETRY_DELAYS[attempt - 1]));
|
|
72
83
|
}
|
|
@@ -75,7 +86,14 @@ async function callConvex(path, method, body, toolName = "unknown", timeoutMs =
|
|
|
75
86
|
res = await attemptConvex(url, method, headers, body, timeoutMs);
|
|
76
87
|
}
|
|
77
88
|
catch (err) {
|
|
78
|
-
|
|
89
|
+
// Ambiguous: the request may never have reached the server, or it may
|
|
90
|
+
// have been received and even processed before the connection died -
|
|
91
|
+
// there's no way to tell from here. Safe to retry for read-only/
|
|
92
|
+
// idempotent calls; for a fund-moving one, retrying risks resending a
|
|
93
|
+
// mutation that already landed.
|
|
94
|
+
lastError = noRetry
|
|
95
|
+
? new Error(`${err?.message ?? err} - request may or may not have completed (no response received). Check status before retrying manually.`)
|
|
96
|
+
: err;
|
|
79
97
|
continue;
|
|
80
98
|
}
|
|
81
99
|
if (res.status === 402) {
|
|
@@ -89,7 +107,7 @@ async function callConvex(path, method, body, toolName = "unknown", timeoutMs =
|
|
|
89
107
|
`Hint: ${b.hint || 'Add FINCH_SESSION_TOKEN=… to the env block in your MCP config'}\n\n` +
|
|
90
108
|
`${b.alternative ? `Alternative: ${b.alternative}` : ""}`);
|
|
91
109
|
}
|
|
92
|
-
if (RETRY_STATUSES.has(res.status) && attempt <
|
|
110
|
+
if (RETRY_STATUSES.has(res.status) && attempt < attempts - 1) {
|
|
93
111
|
// Capture the actual body so a deterministic error (e.g. "unknown
|
|
94
112
|
// token") that happens to come back on a 500 still surfaces its real
|
|
95
113
|
// message if retries exhaust - previously this discarded the body
|
|
@@ -99,6 +117,15 @@ async function callConvex(path, method, body, toolName = "unknown", timeoutMs =
|
|
|
99
117
|
lastError = new Error(`Finch API error ${res.status}: ${bodyText.slice(0, 300) || "(no body)"}`);
|
|
100
118
|
continue;
|
|
101
119
|
}
|
|
120
|
+
// noRetry + a retryable status on the one and only attempt: the server
|
|
121
|
+
// definitely received this one (unlike the network-exception case
|
|
122
|
+
// above), so it may have partially or fully processed it before
|
|
123
|
+
// erroring - same "check before retrying" caution, worded for the case
|
|
124
|
+
// where we know a response did come back.
|
|
125
|
+
if (RETRY_STATUSES.has(res.status) && noRetry) {
|
|
126
|
+
const bodyText = await res.text().catch(() => "");
|
|
127
|
+
throw new Error(`Finch API error ${res.status}: ${bodyText.slice(0, 300) || "(no body)"} - the server received this request and may have processed it before erroring. Check status before retrying manually.`);
|
|
128
|
+
}
|
|
102
129
|
if (!res.ok)
|
|
103
130
|
throw new Error(`Finch API error: ${res.status} ${await res.text()}`);
|
|
104
131
|
return res.json();
|
package/dist/index.js
CHANGED
|
@@ -66,14 +66,17 @@ const C = {
|
|
|
66
66
|
reset: "\x1b[0m",
|
|
67
67
|
bold: "\x1b[1m",
|
|
68
68
|
};
|
|
69
|
+
// Was still spelling out the pre-rebrand "NOELCLAW" wordmark in ASCII art -
|
|
70
|
+
// leftover from before the Finch rebrand, never caught because nothing
|
|
71
|
+
// visually diffs a banner string.
|
|
69
72
|
const BANNER = `
|
|
70
73
|
${C.cyan}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
██║
|
|
76
|
-
╚═╝ ╚═══╝
|
|
74
|
+
███████╗██╗███╗ ██╗ ██████╗██╗ ██╗
|
|
75
|
+
██╔════╝██║████╗ ██║██╔════╝██║ ██║
|
|
76
|
+
█████╗ ██║██╔██╗ ██║██║ ███████║
|
|
77
|
+
██╔══╝ ██║██║╚██╗██║██║ ██╔══██║
|
|
78
|
+
██║ ██║██║ ╚████║╚██████╗██║ ██║
|
|
79
|
+
╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝╚═╝ ╚═╝
|
|
77
80
|
${C.reset}`;
|
|
78
81
|
function line(label, value, color = C.cyan) {
|
|
79
82
|
const pad = " ".repeat(Math.max(0, 12 - label.length));
|
|
@@ -114,9 +117,9 @@ async function main() {
|
|
|
114
117
|
{ label: "Base MCP", match: n => n.startsWith("base_mcp_") },
|
|
115
118
|
{ label: "RH MCP", match: n => n.startsWith("rh_") },
|
|
116
119
|
{ label: "Automation", match: n => /^(create_automation|list_automations|pause_automation|delete_automation|get_automation_runs|run_automation)$/.test(n) },
|
|
117
|
-
{ label: "Vault", match: n => n.startsWith("vault_") },
|
|
120
|
+
{ label: "Vault", match: n => n.startsWith("vault_") || n === "code_session_save" || n === "list_projects" },
|
|
118
121
|
{ label: "Wallet", match: n => /^(get_wallet_address|get_wallet_balance|wallet_sign_message)$/.test(n) },
|
|
119
|
-
{ label: "Staking", match: n => /^(stake_finch|unstake_finch|stake_finch_status|stake_auto_restake)$/.test(n) },
|
|
122
|
+
{ label: "Staking", match: n => /^(stake_finch|unstake_finch|stake_finch_status|stake_auto_restake|claim_vested_rewards)$/.test(n) },
|
|
120
123
|
{ label: "MiroShark", match: n => n.startsWith("miroshark_") },
|
|
121
124
|
{ label: "Scanner", match: n => /^(scan_market|score_token|check_token)$/.test(n) },
|
|
122
125
|
{ label: "Agents", match: n => n.startsWith("agent_") },
|
package/dist/project.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isLocalVaultActive = isLocalVaultActive;
|
|
4
|
+
exports.resolveProjectId = resolveProjectId;
|
|
5
|
+
const convex_js_1 = require("./convex.js");
|
|
6
|
+
const local_vault_js_1 = require("./local-vault.js");
|
|
7
|
+
// MCP has no persistent "active project" UI state the way the webapp Sidebar
|
|
8
|
+
// switcher does - a caller (vault_save, agent_spawn) names the project it
|
|
9
|
+
// wants by string on each call instead. Resolution + auto-create both happen
|
|
10
|
+
// server-side (POST /projects/resolve, see convex/http.ts +
|
|
11
|
+
// convex/projects.ts's resolveOrCreateProjectForUser) so the match/dedup
|
|
12
|
+
// logic exists in exactly one place, shared with the webapp's own project
|
|
13
|
+
// picker.
|
|
14
|
+
//
|
|
15
|
+
// Local-vault mode (vaultBackend: "local") has no project concept at all -
|
|
16
|
+
// it's a flat, unauthenticated file store on the user's own machine with no
|
|
17
|
+
// server to resolve a name against. Callers should check
|
|
18
|
+
// isLocalVaultActive() first and skip resolution entirely rather than call
|
|
19
|
+
// this and get a confusing network-style failure.
|
|
20
|
+
function isLocalVaultActive() {
|
|
21
|
+
return !!(0, local_vault_js_1.getLocalVaultConfig)();
|
|
22
|
+
}
|
|
23
|
+
async function resolveProjectId(name) {
|
|
24
|
+
const trimmed = name.trim();
|
|
25
|
+
if (!trimmed)
|
|
26
|
+
return null;
|
|
27
|
+
try {
|
|
28
|
+
return await (0, convex_js_1.callConvex)("/projects/resolve", "POST", { name: trimmed }, "vault_save");
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Resolution is best-effort - a save should never fail just because the
|
|
32
|
+
// project lookup did. Falls back to unassigned, same as the webapp's
|
|
33
|
+
// agent_spawn membership check when a stale/bad id doesn't match.
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
package/dist/server.js
CHANGED
|
@@ -115,7 +115,7 @@ exports.ALL_TOOLS = [
|
|
|
115
115
|
// SWARM_TOOLS removed v3.19 - multi-agent research is now built into
|
|
116
116
|
// deep_research (depth=standard|deep). Handler fully removed v3.21.
|
|
117
117
|
// FRAMEWORK_TOOLS removed - list_playbooks/run_playbook/get_finch_ledger backend routes never existed, see tools/ import comment above.
|
|
118
|
-
...vault_js_1.VAULT_TOOLS, //
|
|
118
|
+
...vault_js_1.VAULT_TOOLS, // 17 - save, code_session_save, read, list, search, history, diff, export, pin, unpublish, tag, delete, link, related, store_credential, get_credential, list_projects
|
|
119
119
|
...wallet_js_1.WALLET_TOOLS, // 3 - get_wallet_address, get_wallet_balance, wallet_sign_message
|
|
120
120
|
...miroshark_js_1.MIROSHARK_TOOLS, // 3 - simulate, status, stop
|
|
121
121
|
...agents_js_1.AGENT_TOOLS, // 4 - agent_spawn, agent_recall, agent_update, agent_ledger (vault-backed, all working). list_agents/hire_agent and the autonomous-schedule tools (agent_identity/agent_schedule/agent_unschedule/agent_pause/agent_resume/agent_runs) removed - their backend routes were never implemented, see tools/agents.ts
|
|
@@ -139,14 +139,22 @@ exports.ALL_TOOLS = [
|
|
|
139
139
|
...github_js_1.GITHUB_TOOLS, // 8 - list_repos, list_prs, get_pr, list_issues, get_issue, get_file, get_commits, search_code
|
|
140
140
|
...chronicle_js_1.CHRONICLE_TOOLS, // 4 - chronicle_add, chronicle_list, chronicle_search, chronicle_stats
|
|
141
141
|
...packets_js_1.PACKET_TOOLS, // 4 - packet_create, packet_run, packet_list, packet_share
|
|
142
|
-
...stake_js_1.STAKE_TOOLS, //
|
|
143
|
-
// total:
|
|
142
|
+
...stake_js_1.STAKE_TOOLS, // 5 - stake_finch_status, stake_finch, unstake_finch, claim_vested_rewards, stake_auto_restake (custodial wallet; requires `finch login`)
|
|
143
|
+
// total: 116 tools as measured by ALL_TOOLS.length - do not hand-maintain a
|
|
144
144
|
// count in this comment (drifted stale multiple times already: before
|
|
145
145
|
// staking was added, after the P1 audit removed 9 dead framework/agent-
|
|
146
146
|
// schedule tools, after memory_publish was removed for promising a
|
|
147
|
-
// marketplace that doesn't exist,
|
|
148
|
-
//
|
|
149
|
-
//
|
|
147
|
+
// marketplace that doesn't exist, after stake_auto_restake was added,
|
|
148
|
+
// after claim_vested_rewards was added to close the gap where the
|
|
149
|
+
// stake-lifecycle notification told users to "run claimVestedRewards" but
|
|
150
|
+
// no MCP tool by that name existed, after code_session_save was added so
|
|
151
|
+
// coding sessions persist the same way deep_research already auto-saves
|
|
152
|
+
// research, and after list_projects was added alongside `workspaceProject`
|
|
153
|
+
// support on vault_save/agent_spawn - MCP tools can now file into the same
|
|
154
|
+
// Projects the webapp Agents page organizes by, resolved/auto-created
|
|
155
|
+
// server-side via POST /projects/resolve; ALL_TOOLS.length is the only
|
|
156
|
+
// number that can't lie). Per-category counts above are best-effort
|
|
157
|
+
// documentation, not load-bearing anywhere.
|
|
150
158
|
];
|
|
151
159
|
exports.HANDLER_MAP = new Map([
|
|
152
160
|
...market_js_1.MARKET_TOOLS.map(t => [t.name, market_js_1.handleMarketTool]),
|
package/dist/tool-filter.js
CHANGED
|
@@ -27,11 +27,11 @@ exports.filterTools = filterTools;
|
|
|
27
27
|
// through that same preset). New tools MUST match a preset or the test fails
|
|
28
28
|
// the build, instead of quietly vanishing like these did.
|
|
29
29
|
const PRESETS = {
|
|
30
|
-
core: /^(memory_|vault_|agent_|ask_finch|finch_status|finch_diagnostics|finch_shell_chat|get_wallet_address|get_wallet_balance|wallet_sign_message|chronicle_|packet_)/,
|
|
31
|
-
defi: /^(get_market_data|get_token_data|compare_tokens|market_overview|token_history|get_base_token_data|stock_fundamentals|stock_insider|stock_events|market_thesis|trade_plan|base_mcp_|rh_|base_|get_defi_yields|score_token|check_token|scan_market|get_wallet_balance|wallet_sign_message|create_automation|list_automations|pause_automation|delete_automation|get_automation_runs|run_automation|miroshark_|stake_|unstake_finch)/,
|
|
32
|
-
research: /^(memory_|vault_|deep_research|research_compare|research_chain|web_search|web_scrape|schedule_research|list_monitors|cancel_monitor|ask_finch|stock_fundamentals|stock_insider|stock_events)/,
|
|
33
|
-
memory: /^(memory_|vault_|agent_|chronicle_)/,
|
|
34
|
-
coder: /^(audit_contract|github_)/,
|
|
30
|
+
core: /^(memory_|vault_|code_session_save|list_projects|agent_|ask_finch|finch_status|finch_diagnostics|finch_shell_chat|get_wallet_address|get_wallet_balance|wallet_sign_message|chronicle_|packet_)/,
|
|
31
|
+
defi: /^(get_market_data|get_token_data|compare_tokens|market_overview|token_history|get_base_token_data|stock_fundamentals|stock_insider|stock_events|market_thesis|trade_plan|base_mcp_|rh_|base_|get_defi_yields|score_token|check_token|scan_market|get_wallet_balance|wallet_sign_message|create_automation|list_automations|pause_automation|delete_automation|get_automation_runs|run_automation|miroshark_|stake_|unstake_finch|claim_vested_rewards)/,
|
|
32
|
+
research: /^(memory_|vault_|code_session_save|list_projects|deep_research|research_compare|research_chain|web_search|web_scrape|schedule_research|list_monitors|cancel_monitor|ask_finch|stock_fundamentals|stock_insider|stock_events)/,
|
|
33
|
+
memory: /^(memory_|vault_|code_session_save|list_projects|agent_|chronicle_)/,
|
|
34
|
+
coder: /^(audit_contract|github_|code_session_save)/,
|
|
35
35
|
};
|
|
36
36
|
function filterTools(allTools) {
|
|
37
37
|
// Default is "core" - keeps LLM context cost low while everything
|
package/dist/tools/agents.js
CHANGED
|
@@ -7,6 +7,8 @@ const zod_1 = require("zod");
|
|
|
7
7
|
const convex_js_1 = require("../convex.js");
|
|
8
8
|
const llm_js_1 = require("../llm.js");
|
|
9
9
|
const local_vault_js_1 = require("../local-vault.js");
|
|
10
|
+
const memory_js_1 = require("./memory.js");
|
|
11
|
+
const project_js_1 = require("../project.js");
|
|
10
12
|
// ─── Agent Learning Memory (v3.25) ──────────────────────────────────────────
|
|
11
13
|
// After every agent_update, an LLM reviews the new progress in context of the
|
|
12
14
|
// agent's goal + prior learnings to extract a single repeatable insight. The
|
|
@@ -41,7 +43,7 @@ async function extractLearning(goal, priorUpdates, priorLearnings, latest) {
|
|
|
41
43
|
// the catch below swallows that into a silent skip. This early return just
|
|
42
44
|
// avoids building the prompt for a call we already know will fail.
|
|
43
45
|
const hasLLM = !!(process.env.BANKR_API_KEY || process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY || process.env.GROK_API_KEY);
|
|
44
|
-
if (!hasLLM
|
|
46
|
+
if (!hasLLM) {
|
|
45
47
|
return null;
|
|
46
48
|
}
|
|
47
49
|
// Build the user prompt with all the context the model needs.
|
|
@@ -133,6 +135,12 @@ exports.AGENT_TOOLS = [
|
|
|
133
135
|
name: { type: "string", description: "Unique agent name (e.g. 'market-researcher', 'onboarding-helper')" },
|
|
134
136
|
goal: { type: "string", description: "What this agent is trying to accomplish" },
|
|
135
137
|
context: { type: "string", description: "Optional starting context, data, or notes for the agent" },
|
|
138
|
+
workspaceProject: {
|
|
139
|
+
type: "string",
|
|
140
|
+
description: "Optional: file this agent into a named Finch workspace project (visible on the Agents page's " +
|
|
141
|
+
"project switcher). Matched case-insensitively by name; created automatically if it doesn't exist " +
|
|
142
|
+
"yet. Hosted vault only (no effect in local-vault mode).",
|
|
143
|
+
},
|
|
136
144
|
},
|
|
137
145
|
required: ["name", "goal"],
|
|
138
146
|
},
|
|
@@ -140,6 +148,8 @@ exports.AGENT_TOOLS = [
|
|
|
140
148
|
{
|
|
141
149
|
name: "agent_recall",
|
|
142
150
|
description: "Recall a persistent agent by name - loads its goal, current progress, findings, full history, and accumulated learnings (patterns the agent extracted from past runs). " +
|
|
151
|
+
"Also pulls related context from memory/vault (code_session_save entries, deep_research reports, notes) matching the agent's goal, " +
|
|
152
|
+
"so recall reflects everything relevant to the goal - not just what agent_update explicitly logged. " +
|
|
143
153
|
"Use this to resume a long-running task, check what an agent last did, or hand context to a fresh LLM session. " +
|
|
144
154
|
"Learnings compound over time - the more an agent runs, the smarter recall becomes.",
|
|
145
155
|
inputSchema: {
|
|
@@ -192,6 +202,7 @@ const SpawnAgentSchema = zod_1.z.object({
|
|
|
192
202
|
name: zod_1.z.string().min(1).max(60).regex(/^[a-z0-9-]+$/, "name must be lowercase alphanumeric with hyphens"),
|
|
193
203
|
goal: zod_1.z.string().min(1),
|
|
194
204
|
context: zod_1.z.string().optional(),
|
|
205
|
+
workspaceProject: zod_1.z.string().optional(),
|
|
195
206
|
});
|
|
196
207
|
const RecallAgentSchema = zod_1.z.object({ name: zod_1.z.string().min(1) });
|
|
197
208
|
const UpdateAgentSchema = zod_1.z.object({
|
|
@@ -217,8 +228,8 @@ async function handleAgentTool(name, args) {
|
|
|
217
228
|
if (name === "agent_spawn") {
|
|
218
229
|
const parsed = SpawnAgentSchema.safeParse(args);
|
|
219
230
|
if (!parsed.success)
|
|
220
|
-
return { content: [{ type: "text", text:
|
|
221
|
-
const { name: agentName, goal, context } = parsed.data;
|
|
231
|
+
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
232
|
+
const { name: agentName, goal, context, workspaceProject } = parsed.data;
|
|
222
233
|
const content = JSON.stringify({
|
|
223
234
|
goal,
|
|
224
235
|
status: "active",
|
|
@@ -237,19 +248,30 @@ async function handleAgentTool(name, args) {
|
|
|
237
248
|
commitMsg: "spawned",
|
|
238
249
|
};
|
|
239
250
|
const localVault = (0, local_vault_js_1.getLocalVaultConfig)();
|
|
251
|
+
// Resolve the project name -> id server-side (auto-creates on first use).
|
|
252
|
+
// No-op in local-vault mode - there is no project concept there.
|
|
253
|
+
let resolvedProjectName = null;
|
|
254
|
+
if (workspaceProject && !localVault) {
|
|
255
|
+
const resolved = await (0, project_js_1.resolveProjectId)(workspaceProject);
|
|
256
|
+
if (resolved) {
|
|
257
|
+
savePayload.projectId = resolved.projectId;
|
|
258
|
+
resolvedProjectName = resolved.name;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
240
261
|
const data = localVault
|
|
241
262
|
? (0, local_vault_js_1.localVaultSave)(localVault, savePayload)
|
|
242
263
|
: await (0, convex_js_1.callConvex)("/vault/save", "POST", savePayload, "vault_save");
|
|
243
264
|
if (data.error)
|
|
244
265
|
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
266
|
+
const projectLine = resolvedProjectName ? `\n**Project:** ${resolvedProjectName}` : "";
|
|
245
267
|
return {
|
|
246
|
-
content: [{ type: "text", text: `🤖 Agent **${agentName}** spawned${localVault ? " locally" : ""}.\n\n**Goal:** ${goal}\n\nRecall with \`agent_recall\` · Update progress with \`agent_update\`` }],
|
|
268
|
+
content: [{ type: "text", text: `🤖 Agent **${agentName}** spawned${localVault ? " locally" : ""}.\n\n**Goal:** ${goal}${projectLine}\n\nRecall with \`agent_recall\` · Update progress with \`agent_update\`` }],
|
|
247
269
|
};
|
|
248
270
|
}
|
|
249
271
|
if (name === "agent_recall") {
|
|
250
272
|
const parsed = RecallAgentSchema.safeParse(args);
|
|
251
273
|
if (!parsed.success)
|
|
252
|
-
return { content: [{ type: "text", text:
|
|
274
|
+
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
253
275
|
const localVault = (0, local_vault_js_1.getLocalVaultConfig)();
|
|
254
276
|
let data;
|
|
255
277
|
try {
|
|
@@ -291,12 +313,30 @@ async function handleAgentTool(name, args) {
|
|
|
291
313
|
lines.push(`\n**Recent updates:**\n${updates.join("\n")}`);
|
|
292
314
|
if (state.nextStep)
|
|
293
315
|
lines.push(`\n**Next step:** ${state.nextStep}`);
|
|
316
|
+
// Related context - best-effort pull of relevant memory/vault knowledge
|
|
317
|
+
// (code_session_save entries, deep_research reports, manual notes) so the
|
|
318
|
+
// agent isn't blind to work done on its goal outside its own update log.
|
|
319
|
+
// This is what makes recall "continuously have context" rather than only
|
|
320
|
+
// ever knowing what agent_update explicitly logged.
|
|
321
|
+
if (state.goal) {
|
|
322
|
+
try {
|
|
323
|
+
const related = await (0, memory_js_1.hybridMemorySearch)(state.goal, 4);
|
|
324
|
+
if (related.length) {
|
|
325
|
+
lines.push(`\n**📎 Related context (${related.length}):**`);
|
|
326
|
+
related.forEach((r) => {
|
|
327
|
+
const title = r.metadata?.title ?? r.content.slice(0, 70).replace(/\n/g, " ");
|
|
328
|
+
lines.push(` • ${title}`);
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
catch { /* best-effort - recall must never fail because of this */ }
|
|
333
|
+
}
|
|
294
334
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
295
335
|
}
|
|
296
336
|
if (name === "agent_update") {
|
|
297
337
|
const parsed = UpdateAgentSchema.safeParse(args);
|
|
298
338
|
if (!parsed.success)
|
|
299
|
-
return { content: [{ type: "text", text:
|
|
339
|
+
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
300
340
|
const { name: agentName, progress, findings, status = "active", nextStep } = parsed.data;
|
|
301
341
|
return withAgentLock(agentName, async () => {
|
|
302
342
|
const localVault = (0, local_vault_js_1.getLocalVaultConfig)();
|