@finchagentic/mcp 4.1.0 → 4.2.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.
@@ -1,14 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AGENT_TOOLS = void 0;
4
- exports.buildAgentList = buildAgentList;
5
4
  exports.buildAgentLedger = buildAgentLedger;
6
- exports.buildAgentRuns = buildAgentRuns;
7
5
  exports.handleAgentTool = handleAgentTool;
8
6
  const zod_1 = require("zod");
9
- const ethers_1 = require("ethers");
10
7
  const convex_js_1 = require("../convex.js");
11
8
  const llm_js_1 = require("../llm.js");
9
+ const local_vault_js_1 = require("../local-vault.js");
12
10
  // ─── Agent Learning Memory (v3.25) ──────────────────────────────────────────
13
11
  // After every agent_update, an LLM reviews the new progress in context of the
14
12
  // agent's goal + prior learnings to extract a single repeatable insight. The
@@ -42,7 +40,7 @@ async function extractLearning(goal, priorUpdates, priorLearnings, latest) {
42
40
  // immediately rather than proxying through Finch (BYOK is required), and
43
41
  // the catch below swallows that into a silent skip. This early return just
44
42
  // avoids building the prompt for a call we already know will fail.
45
- const hasLLM = !!(process.env.BANKR_API_KEY || process.env.ANTHROPIC_API_KEY || process.env.GROK_API_KEY);
43
+ const hasLLM = !!(process.env.BANKR_API_KEY || process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY || process.env.GROK_API_KEY);
46
44
  if (!hasLLM && !process.env.FINCH_SESSION_TOKEN) {
47
45
  return null;
48
46
  }
@@ -104,42 +102,24 @@ function withAgentLock(agentName, fn) {
104
102
  });
105
103
  return next;
106
104
  }
105
+ // list_agents/hire_agent were removed (not disabled - deleted) because their
106
+ // backend routes, /agents/list and /agents/hire, were never actually
107
+ // registered in app/convex/http.ts - only dangling section-header comments
108
+ // exist there, no matching http.route(...) call. Every call to either tool
109
+ // always 404'd. Re-add them (and the matching handler blocks below) only
110
+ // alongside building those two routes for real.
111
+ //
112
+ // Same story, same fix, applied again here: agent_identity, agent_schedule,
113
+ // agent_unschedule, agent_pause, agent_resume and agent_runs called
114
+ // /agents/identity, /agents/schedule, /agents/unschedule, /agents/pause and
115
+ // /agents/runs - none of which exist in http.ts, and none of which have any
116
+ // backing data model in schema.ts (no agent-identity table, no scheduler,
117
+ // no run-history table; there isn't even a cron for it in crons.ts). This
118
+ // isn't a missing route, it's an unbuilt subsystem, so the tools were
119
+ // removed rather than stubbed. Re-add only alongside building that
120
+ // autonomous-scheduling backend for real: identity/schedule storage, a cron
121
+ // that actually wakes agents up, and run-history writes.
107
122
  exports.AGENT_TOOLS = [
108
- {
109
- name: "list_agents",
110
- description: "List all available specialist agents you can hire - built-in experts (analyst, risk-manager, researcher, executor, scout) plus any community-published agents.",
111
- inputSchema: { type: "object", properties: {}, required: [] },
112
- },
113
- {
114
- name: "hire_agent",
115
- description: "Load a specialist agent's expertise and apply it to a task YOURSELF — the tool returns the agent's " +
116
- "full persona (its framework, thresholds and house rules) scoped to your task, and you answer in that " +
117
- "voice. Built-in: analyst, risk-manager, researcher, executor, scout; use list_agents for the rest. " +
118
- "No API key needed. Pass `execute: true` only when you need the agent's own model to answer instead " +
119
- "(costs a server-side LLM call and you cannot steer the reasoning).",
120
- inputSchema: {
121
- type: "object",
122
- properties: {
123
- agentId: {
124
- type: "string",
125
- description: "Agent ID from list_agents. Built-in: analyst, risk-manager, researcher, executor, scout. Or a custom agent ID.",
126
- },
127
- task: {
128
- type: "string",
129
- description: "The task or question for the agent. Be specific - better input = better output.",
130
- },
131
- execute: {
132
- type: "boolean",
133
- description: "Run the agent server-side instead of returning its persona for you to apply. Default false. Requires a server LLM key.",
134
- },
135
- maxTokens: {
136
- type: "number",
137
- description: "execute:true only. Max response tokens (default 800, max 1200).",
138
- },
139
- },
140
- required: ["agentId", "task"],
141
- },
142
- },
143
123
  {
144
124
  name: "agent_spawn",
145
125
  description: "Create a persistent NAMED agent with a goal - survives across sessions, state saved to vault under `agent/<name>` key. " +
@@ -187,27 +167,6 @@ exports.AGENT_TOOLS = [
187
167
  required: ["name", "progress"],
188
168
  },
189
169
  },
190
- {
191
- name: "agent_identity",
192
- description: "Get or create a persistent on-chain identity (wallet address) for a named agent. " +
193
- "Every agent gets a unique Base address that acts as its digital identity - visible in the app, " +
194
- "usable for receiving payments, and permanently tied to that agent name. " +
195
- "Call once per agent; calling again returns the same address.",
196
- inputSchema: {
197
- type: "object",
198
- properties: {
199
- agentId: {
200
- type: "string",
201
- description: "Agent ID or name (e.g. 'market-researcher', 'analyst')",
202
- },
203
- agentName: {
204
- type: "string",
205
- description: "Human-readable agent name for display (optional)",
206
- },
207
- },
208
- required: ["agentId"],
209
- },
210
- },
211
170
  {
212
171
  name: "agent_ledger",
213
172
  description: "View the full activity ledger for a persistent agent - every update, status change, and finding logged in order. " +
@@ -228,79 +187,7 @@ exports.AGENT_TOOLS = [
228
187
  required: ["name"],
229
188
  },
230
189
  },
231
- {
232
- name: "agent_schedule",
233
- description: "Attach an autonomous schedule to an existing agent. The agent wakes up on cron cadence, executes a workflow, " +
234
- "saves the result to vault under `agent/<name>/runs/<date>`, and optionally pings you on Telegram. " +
235
- "Workflows: " +
236
- "deep_research (LLM brief on agent's goal) · " +
237
- "packet (run a named packet) · " +
238
- "llm (agentic loop with restricted tools) · " +
239
- "reflection (walk recent vault activity → update profile/state for persistent context). " +
240
- "Cron shorthand: hourly | daily | weekly | every-2-minutes | every-5-minutes (last two for testing).",
241
- inputSchema: {
242
- type: "object",
243
- properties: {
244
- name: { type: "string", description: "Agent name (must already exist via agent_spawn)" },
245
- cron: { type: "string", description: "Schedule: hourly | daily | weekly | every-2-minutes | every-5-minutes" },
246
- workflow: { type: "string", enum: ["deep_research", "packet", "llm", "reflection"], description: "Workflow type" },
247
- topic: { type: "string", description: "[deep_research] Research topic (defaults to agent's goal)" },
248
- packetName: { type: "string", description: "[packet] Name of packet to run (must exist via packet_create)" },
249
- prompt: { type: "string", description: "[llm] Instructions for the autonomous agent loop" },
250
- allowedTools: { type: "array", items: { type: "string" }, description: "[llm] Tool whitelist - subset of: vault_save, vault_search, get_market_data, web_search" },
251
- windowDays: { type: "number", description: "[reflection] Days of vault activity to walk (default 7)" },
252
- maxRunsPerDay: { type: "number", description: "Cost ceiling - default 4 (daily), 24 (hourly), 1 (weekly)" },
253
- },
254
- required: ["name", "cron", "workflow"],
255
- },
256
- },
257
- {
258
- name: "agent_unschedule",
259
- description: "Remove the autonomous schedule from an agent. The agent itself remains in vault - only its scheduled execution is cleared.",
260
- inputSchema: {
261
- type: "object",
262
- properties: { name: { type: "string", description: "Agent name" } },
263
- required: ["name"],
264
- },
265
- },
266
- {
267
- name: "agent_pause",
268
- description: "Pause an agent's autonomous schedule without deleting it. Use agent_resume to re-enable.",
269
- inputSchema: {
270
- type: "object",
271
- properties: { name: { type: "string", description: "Agent name" } },
272
- required: ["name"],
273
- },
274
- },
275
- {
276
- name: "agent_resume",
277
- description: "Re-enable a paused agent's schedule. Resets the consecutive-failure counter.",
278
- inputSchema: {
279
- type: "object",
280
- properties: { name: { type: "string", description: "Agent name" } },
281
- required: ["name"],
282
- },
283
- },
284
- {
285
- name: "agent_runs",
286
- description: "View recent autonomous run history for a scheduled agent - when it ran, success/failure status, " +
287
- "vault key where the output was saved, and duration. Use this to verify the autonomous loop is healthy.",
288
- inputSchema: {
289
- type: "object",
290
- properties: {
291
- name: { type: "string", description: "Agent name" },
292
- limit: { type: "number", description: "Max runs to return (default 10, max 50)" },
293
- },
294
- required: ["name"],
295
- },
296
- },
297
190
  ];
298
- const HireAgentSchema = zod_1.z.object({
299
- agentId: zod_1.z.string().min(1),
300
- task: zod_1.z.string().min(1),
301
- execute: zod_1.z.boolean().optional(),
302
- maxTokens: zod_1.z.number().int().min(100).max(1200).optional(),
303
- });
304
191
  const SpawnAgentSchema = zod_1.z.object({
305
192
  name: zod_1.z.string().min(1).max(60).regex(/^[a-z0-9-]+$/, "name must be lowercase alphanumeric with hyphens"),
306
193
  goal: zod_1.z.string().min(1),
@@ -315,19 +202,6 @@ const UpdateAgentSchema = zod_1.z.object({
315
202
  nextStep: zod_1.z.string().optional(),
316
203
  });
317
204
  // ── Structured output builders (schemas in output-schemas.ts) ───────────────
318
- function buildAgentList(agents) {
319
- return {
320
- count: agents.length,
321
- agents: agents.map((a) => ({
322
- id: a.id,
323
- name: a.name,
324
- description: a.description ?? null,
325
- category: a.category ?? null,
326
- pricingType: a.pricingType ?? null,
327
- runs: a.runs ?? null,
328
- })),
329
- };
330
- }
331
205
  function buildAgentLedger(name, versions) {
332
206
  return {
333
207
  name,
@@ -339,97 +213,7 @@ function buildAgentLedger(name, versions) {
339
213
  })),
340
214
  };
341
215
  }
342
- function buildAgentRuns(name, runs) {
343
- return {
344
- name,
345
- count: runs.length,
346
- runs: runs.map((r) => ({
347
- startedAt: r.startedAt ?? null,
348
- endedAt: r.endedAt ?? null,
349
- status: r.status ?? null,
350
- workflow: r.workflow ?? null,
351
- durationMs: r.durationMs ?? null,
352
- toolCallCount: r.toolCallCount ?? null,
353
- resultSummary: r.resultSummary ?? null,
354
- errorMsg: r.errorMsg ?? null,
355
- })),
356
- };
357
- }
358
216
  async function handleAgentTool(name, args) {
359
- if (name === "list_agents") {
360
- const data = await (0, convex_js_1.callConvex)("/agents/list", "GET", undefined, "list_agents");
361
- const agents = data.agents ?? [];
362
- const lines = agents.map((a) => {
363
- const badge = a.pricingType === "free" ? "free" : "token-based";
364
- const runs = a.runs != null ? ` · ${a.runs} runs` : "";
365
- return `**${a.name}** (\`${a.id}\`) [${badge}${runs}]\n ${a.description}`;
366
- });
367
- return {
368
- content: [{
369
- type: "text",
370
- text: `## Available Agents (${agents.length})\n\n${lines.join("\n\n")}`,
371
- }],
372
- structuredContent: buildAgentList(agents),
373
- };
374
- }
375
- if (name === "hire_agent") {
376
- const parsed = HireAgentSchema.safeParse(args);
377
- if (!parsed.success) {
378
- return {
379
- content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }],
380
- isError: true,
381
- };
382
- }
383
- const { agentId, task, maxTokens, execute = false } = parsed.data;
384
- const data = await (0, convex_js_1.callConvex)("/agents/hire", "POST", { agentId, task, maxTokens, mode: execute ? "run" : "brief" }, "hire_agent");
385
- if (data.error) {
386
- return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
387
- }
388
- if (execute) {
389
- const footer = data.tokensUsed ? `\n\n*Tokens used: ${data.tokensUsed}*` : "";
390
- return {
391
- content: [{
392
- type: "text",
393
- text: `## ${data.agent ?? agentId} - Response\n\n${data.result}${footer}`,
394
- }],
395
- };
396
- }
397
- // The registry lookup is the tool's real work - it holds each agent's
398
- // persona, thresholds and hard rules. Applying them is model work, and the
399
- // caller's model is the better one *and* the one the user can redirect.
400
- if (!data.systemPrompt) {
401
- return {
402
- content: [{
403
- type: "text",
404
- text: `Agent \`${agentId}\` returned no persona. The backend may predate brief mode — retry with \`execute: true\`.`,
405
- }],
406
- isError: true,
407
- };
408
- }
409
- return {
410
- content: [{
411
- type: "text",
412
- text: [
413
- `## Adopt: ${data.agent ?? agentId}`,
414
- ``,
415
- `You are now this specialist. Answer the task below **in this persona**, applying its framework, ` +
416
- `thresholds and hard rules exactly as written — including the ones that tell you to refuse, ` +
417
- `skip or downgrade a setup. Those gates are the reason the persona exists; do not soften them ` +
418
- `to give a more agreeable answer.`,
419
- ``,
420
- `---`,
421
- ``,
422
- data.systemPrompt,
423
- ``,
424
- `---`,
425
- ``,
426
- `### Task`,
427
- ``,
428
- task,
429
- ].join("\n"),
430
- }],
431
- };
432
- }
433
217
  if (name === "agent_spawn") {
434
218
  const parsed = SpawnAgentSchema.safeParse(args);
435
219
  if (!parsed.success)
@@ -442,7 +226,7 @@ async function handleAgentTool(name, args) {
442
226
  context: context ?? null,
443
227
  updates: [],
444
228
  }, null, 2);
445
- const data = await (0, convex_js_1.callConvex)("/vault/save", "POST", {
229
+ const savePayload = {
446
230
  type: "memory",
447
231
  key: `agent/${agentName}`,
448
232
  title: `Agent: ${agentName}`,
@@ -451,18 +235,31 @@ async function handleAgentTool(name, args) {
451
235
  agentId: agentName,
452
236
  tags: ["persistent-agent"],
453
237
  commitMsg: "spawned",
454
- }, "vault_save");
238
+ };
239
+ const localVault = (0, local_vault_js_1.getLocalVaultConfig)();
240
+ const data = localVault
241
+ ? (0, local_vault_js_1.localVaultSave)(localVault, savePayload)
242
+ : await (0, convex_js_1.callConvex)("/vault/save", "POST", savePayload, "vault_save");
455
243
  if (data.error)
456
244
  return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
457
245
  return {
458
- content: [{ type: "text", text: `🤖 Agent **${agentName}** spawned.\n\n**Goal:** ${goal}\n\nRecall with \`agent_recall\` · Update progress with \`agent_update\`` }],
246
+ content: [{ type: "text", text: `🤖 Agent **${agentName}** spawned${localVault ? " locally" : ""}.\n\n**Goal:** ${goal}\n\nRecall with \`agent_recall\` · Update progress with \`agent_update\`` }],
459
247
  };
460
248
  }
461
249
  if (name === "agent_recall") {
462
250
  const parsed = RecallAgentSchema.safeParse(args);
463
251
  if (!parsed.success)
464
252
  return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
465
- const data = await (0, convex_js_1.callConvex)(`/vault/entry?key=agent/${parsed.data.name}`, "GET", undefined, "vault_read");
253
+ const localVault = (0, local_vault_js_1.getLocalVaultConfig)();
254
+ let data;
255
+ try {
256
+ data = localVault
257
+ ? (0, local_vault_js_1.localVaultRead)(localVault, `agent/${parsed.data.name}`)
258
+ : await (0, convex_js_1.callConvex)(`/vault/entry?key=agent/${parsed.data.name}`, "GET", undefined, "vault_read");
259
+ }
260
+ catch {
261
+ data = { error: "not found" };
262
+ }
466
263
  if (data.error || !data.content) {
467
264
  return { content: [{ type: "text", text: `Agent \`${parsed.data.name}\` not found. Spawn it first with \`agent_spawn\`.` }], isError: true };
468
265
  }
@@ -502,9 +299,18 @@ async function handleAgentTool(name, args) {
502
299
  return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
503
300
  const { name: agentName, progress, findings, status = "active", nextStep } = parsed.data;
504
301
  return withAgentLock(agentName, async () => {
302
+ const localVault = (0, local_vault_js_1.getLocalVaultConfig)();
505
303
  // Load current state - inside the lock so two parallel updates can't
506
304
  // both read v=N and both write v=N+1 (the second silently loses).
507
- const current = await (0, convex_js_1.callConvex)(`/vault/entry?key=agent/${agentName}`, "GET", undefined, "vault_read");
305
+ let current;
306
+ try {
307
+ current = localVault
308
+ ? (0, local_vault_js_1.localVaultRead)(localVault, `agent/${agentName}`)
309
+ : await (0, convex_js_1.callConvex)(`/vault/entry?key=agent/${agentName}`, "GET", undefined, "vault_read");
310
+ }
311
+ catch {
312
+ current = { error: "not found" };
313
+ }
508
314
  if (current.error || !current.content) {
509
315
  return { content: [{ type: "text", text: `Agent \`${agentName}\` not found. Spawn it first.` }], isError: true };
510
316
  }
@@ -534,7 +340,7 @@ async function handleAgentTool(name, args) {
534
340
  const entry = { learned: newLearning, ts: Date.now(), fromUpdate: state.updates.length - 1 };
535
341
  state.learnings = [...(state.learnings ?? []), entry].slice(-MAX_LEARNINGS);
536
342
  }
537
- const data = await (0, convex_js_1.callConvex)("/vault/save", "POST", {
343
+ const savePayload = {
538
344
  type: "memory",
539
345
  key: `agent/${agentName}`,
540
346
  title: `Agent: ${agentName}`,
@@ -542,7 +348,10 @@ async function handleAgentTool(name, args) {
542
348
  contentType: "json",
543
349
  agentId: agentName,
544
350
  commitMsg: `[${status}] ${progress.slice(0, 60)}${newLearning ? " · +learning" : ""}`,
545
- }, "vault_save");
351
+ };
352
+ const data = localVault
353
+ ? (0, local_vault_js_1.localVaultSave)(localVault, savePayload)
354
+ : await (0, convex_js_1.callConvex)("/vault/save", "POST", savePayload, "vault_save");
546
355
  if (data.error)
547
356
  return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
548
357
  const statusEmoji = status === "complete" ? "✅" : status === "blocked" ? "🚫" : "🔄";
@@ -559,10 +368,19 @@ async function handleAgentTool(name, args) {
559
368
  if (!agentName)
560
369
  return { content: [{ type: "text", text: "name is required" }], isError: true };
561
370
  const cap = Math.min(Math.max(1, limit), 50);
562
- const data = await (0, convex_js_1.callConvex)(`/vault/history?key=agent/${encodeURIComponent(agentName)}&limit=${cap}`, "GET", undefined, "vault_history");
371
+ const localVault = (0, local_vault_js_1.getLocalVaultConfig)();
372
+ let data;
373
+ try {
374
+ data = localVault
375
+ ? (0, local_vault_js_1.localVaultHistory)(localVault, `agent/${agentName}`)
376
+ : await (0, convex_js_1.callConvex)(`/vault/history?key=agent/${encodeURIComponent(agentName)}&limit=${cap}`, "GET", undefined, "vault_history");
377
+ }
378
+ catch {
379
+ data = { history: [] };
380
+ }
563
381
  if (data.error)
564
382
  return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
565
- const versions = data.history ?? [];
383
+ const versions = (data.history ?? []).slice(0, cap);
566
384
  if (!versions.length) {
567
385
  return { content: [{ type: "text", text: `No ledger entries found for agent \`${agentName}\`. Spawn it first with \`agent_spawn\`.` }], structuredContent: buildAgentLedger(agentName, []) };
568
386
  }
@@ -579,151 +397,5 @@ async function handleAgentTool(name, args) {
579
397
  structuredContent: buildAgentLedger(agentName, versions),
580
398
  };
581
399
  }
582
- if (name === "agent_identity") {
583
- const { agentId, agentName } = args;
584
- // Check if identity already exists
585
- const existing = await (0, convex_js_1.callConvex)(`/agents/identity?agentId=${encodeURIComponent(agentId)}`, "GET", undefined, "agent_identity");
586
- if (existing?.identity) {
587
- const id = existing.identity;
588
- return {
589
- content: [{
590
- type: "text",
591
- text: [
592
- `🤖 **Agent Identity: \`${agentId}\`**`,
593
- ``,
594
- `**Address:** \`${id.walletAddress}\``,
595
- `**Network:** Base mainnet`,
596
- ``,
597
- `This address is permanent and tied to this agent.`,
598
- `Share it to receive funds · Track spending · Verify agent actions`,
599
- ].join("\n"),
600
- }],
601
- };
602
- }
603
- // Generate new wallet address for the agent
604
- const wallet = ethers_1.ethers.Wallet.createRandom();
605
- await (0, convex_js_1.callConvex)("/agents/identity", "POST", {
606
- agentId,
607
- walletAddress: wallet.address,
608
- agentName: agentName ?? agentId,
609
- }, "agent_identity");
610
- return {
611
- content: [{
612
- type: "text",
613
- text: [
614
- `🤖 **Agent Identity Created: \`${agentId}\`**`,
615
- ``,
616
- `**Address:** \`${wallet.address}\``,
617
- `**Network:** Base mainnet`,
618
- ``,
619
- `This identity is now permanently linked to \`${agentId}\`.`,
620
- `Visible in the Agents page of your Finch app.`,
621
- ].join("\n"),
622
- }],
623
- };
624
- }
625
- // ─── Autonomous schedule tools ──────────────────────────────────────────────
626
- if (name === "agent_schedule") {
627
- const a = args;
628
- if (!a.name || !a.cron || !a.workflow) {
629
- return { content: [{ type: "text", text: "name, cron, workflow required" }], isError: true };
630
- }
631
- const agentKey = `agent/${a.name}`;
632
- const workflowConfig = {};
633
- if (a.workflow === "deep_research" && a.topic)
634
- workflowConfig.topic = a.topic;
635
- if (a.workflow === "packet")
636
- workflowConfig.packetName = a.packetName;
637
- if (a.workflow === "llm") {
638
- workflowConfig.prompt = a.prompt;
639
- workflowConfig.allowedTools = a.allowedTools ?? ["vault_save", "vault_search"];
640
- }
641
- if (a.workflow === "reflection") {
642
- workflowConfig.windowDays = typeof a.windowDays === "number" ? a.windowDays : 7;
643
- }
644
- const result = await (0, convex_js_1.callConvex)("/agents/schedule", "POST", {
645
- agentKey, cron: a.cron, workflow: a.workflow, workflowConfig,
646
- maxRunsPerDay: a.maxRunsPerDay,
647
- }, "agent_schedule");
648
- if (result.error)
649
- return { content: [{ type: "text", text: `Schedule failed: ${result.error}` }], isError: true };
650
- return {
651
- content: [{
652
- type: "text",
653
- text: [
654
- `🤖 **Schedule attached to \`${a.name}\`**`,
655
- ``,
656
- `Workflow: ${a.workflow}${a.topic ? `\nTopic: ${a.topic}` : ""}${a.packetName ? `\nPacket: ${a.packetName}` : ""}`,
657
- `Cron: ${a.cron}`,
658
- `Next run: ${result.nextRunIso}`,
659
- ``,
660
- `The agent will run autonomously. Check progress with \`agent_runs name=${a.name}\`.`,
661
- ].filter(Boolean).join("\n"),
662
- }],
663
- };
664
- }
665
- if (name === "agent_unschedule") {
666
- const a = args;
667
- if (!a.name)
668
- return { content: [{ type: "text", text: "name required" }], isError: true };
669
- const result = await (0, convex_js_1.callConvex)("/agents/unschedule", "POST", { agentKey: `agent/${a.name}` }, "agent_unschedule");
670
- if (result.error)
671
- return { content: [{ type: "text", text: `Unschedule failed: ${result.error}` }], isError: true };
672
- return {
673
- content: [{
674
- type: "text",
675
- text: result.deleted
676
- ? `🗑️ Schedule removed from \`${a.name}\`. Agent itself remains in vault.`
677
- : `\`${a.name}\` had no active schedule.`,
678
- }],
679
- };
680
- }
681
- if (name === "agent_pause" || name === "agent_resume") {
682
- const a = args;
683
- if (!a.name)
684
- return { content: [{ type: "text", text: "name required" }], isError: true };
685
- const enabled = name === "agent_resume";
686
- const result = await (0, convex_js_1.callConvex)("/agents/pause", "POST", { agentKey: `agent/${a.name}`, enabled }, name);
687
- if (result.error)
688
- return { content: [{ type: "text", text: `${name} failed: ${result.error}` }], isError: true };
689
- if (!result.changed)
690
- return { content: [{ type: "text", text: `\`${a.name}\` has no schedule to ${enabled ? "resume" : "pause"}.` }] };
691
- return {
692
- content: [{
693
- type: "text",
694
- text: enabled
695
- ? `▶️ Resumed schedule for \`${a.name}\`. Failure counter reset.`
696
- : `⏸️ Paused schedule for \`${a.name}\`. Use \`agent_resume name=${a.name}\` to re-enable.`,
697
- }],
698
- };
699
- }
700
- if (name === "agent_runs") {
701
- const a = args;
702
- if (!a.name)
703
- return { content: [{ type: "text", text: "name required" }], isError: true };
704
- const limit = Math.min(Math.max(a.limit ?? 10, 1), 50);
705
- const result = await (0, convex_js_1.callConvex)(`/agents/runs?agentKey=${encodeURIComponent(`agent/${a.name}`)}&limit=${limit}`, "GET", undefined, "agent_runs");
706
- if (result.error)
707
- return { content: [{ type: "text", text: `agent_runs failed: ${result.error}` }], isError: true };
708
- const runs = (result.runs ?? []);
709
- if (!runs.length) {
710
- return { content: [{ type: "text", text: `No autonomous runs yet for \`${a.name}\`. The agent runs on its cron schedule - be patient.` }], structuredContent: buildAgentRuns(a.name, []) };
711
- }
712
- const lines = [`## Recent runs for \`${a.name}\` (${runs.length})`, ""];
713
- for (const r of runs) {
714
- const when = new Date(r.startedAt).toISOString().replace("T", " ").slice(0, 19);
715
- const dur = r.durationMs ? `${Math.round(r.durationMs / 1000)}s` : "-";
716
- const icon = r.status === "success" ? "✅" : r.status === "failed" ? "❌" : r.status === "timeout" ? "⏱️" : "⏳";
717
- lines.push(`${icon} **${when}** · ${r.workflow} · ${dur}${r.toolCallCount != null ? ` · ${r.toolCallCount} tool calls` : ""}`);
718
- if (r.vaultKeyResult)
719
- lines.push(` → \`${r.vaultKeyResult}\``);
720
- if (r.resultSummary)
721
- lines.push(` ${r.resultSummary.slice(0, 200)}`);
722
- if (r.errorMsg)
723
- lines.push(` ⚠️ ${r.errorMsg.slice(0, 200)}`);
724
- lines.push("");
725
- }
726
- return { content: [{ type: "text", text: lines.join("\n") }], structuredContent: buildAgentRuns(a.name, runs) };
727
- }
728
400
  return null;
729
401
  }
@@ -306,9 +306,9 @@ async function resolveBasename(input) {
306
306
  *
307
307
  * `base_mcp_send` and `base_mcp_swap` sign and broadcast from the user's wallet
308
308
  * on the first call — an on-chain transfer that cannot be recalled. The chain's
309
- * own swap tool (`rh_mcp_swap`) already required confirmation, as do
310
- * `vault_delete` and `memory_publish`; these two were the outliers, moving real
311
- * funds with less friction than sharing a workflow. The guard also gives the
309
+ * own swap tool (`rh_mcp_swap`) already required confirmation, as does
310
+ * `vault_delete`; these two were the outliers, moving real
311
+ * funds with less friction than an irreversible delete. The guard also gives the
312
312
  * model somewhere to stop when a destination address came from a scraped page
313
313
  * or tool output rather than from the user.
314
314
  */
@@ -460,18 +460,6 @@ async function handleBaseMcpTool(name, args) {
460
460
  structuredContent: buildBasenameResolution(a.name, result),
461
461
  };
462
462
  }
463
- case "base_mcp_analyze": {
464
- // Resolve basename if input is a name
465
- let address = a.address;
466
- if (typeof a.address === "string" && !/^0x[a-fA-F0-9]{40}$/.test(a.address)) {
467
- const resolved = await resolveBasename(a.address);
468
- if (resolved.error || !resolved.address) {
469
- return { content: [{ type: "text", text: `Could not resolve "${a.address}": ${resolved.error ?? "unknown"}` }], isError: true };
470
- }
471
- address = resolved.address;
472
- }
473
- return (0, defi_js_1.handleDefiTool)("analyze_wallet", { address, label: a.label });
474
- }
475
463
  default:
476
464
  return null;
477
465
  }