@noelclaw/mcp 2.3.0 → 2.4.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/dist/index.js +10 -9
- package/dist/server.js +38 -28
- package/dist/tools/automation.js +38 -0
- package/dist/tools/coder.js +62 -0
- package/dist/tools/defi.js +127 -0
- package/dist/tools/humanizer.js +143 -2
- package/dist/tools/insight.js +197 -19
- package/dist/tools/market.js +182 -7
- package/dist/tools/memory.js +198 -170
- package/dist/tools/miroshark.js +15 -4
- package/dist/tools/os.js +223 -0
- package/dist/tools/scanner.js +183 -52
- package/dist/tools/swarm.js +327 -14
- package/dist/tools/vault.js +94 -153
- package/package.json +5 -2
- package/dist/tools/news.js +0 -6
- package/dist/tools/research.js +0 -8
- package/dist/tools/twitter.js +0 -67
package/dist/tools/swarm.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.handleSwarmTool = handleSwarmTool;
|
|
|
5
5
|
const zod_1 = require("zod");
|
|
6
6
|
const convex_js_1 = require("../convex.js");
|
|
7
7
|
const market_js_1 = require("./market.js");
|
|
8
|
+
const memory_js_1 = require("./memory.js");
|
|
8
9
|
function formatDate(ts) {
|
|
9
10
|
return new Date(ts).toUTCString();
|
|
10
11
|
}
|
|
@@ -82,21 +83,22 @@ exports.SWARM_TOOLS = [
|
|
|
82
83
|
{
|
|
83
84
|
name: "trigger_agent",
|
|
84
85
|
description: "Run a single swarm agent immediately — automatically starts the swarm if needed. " +
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
86
|
+
"Agents: market-monitor (live prices), sentiment-tracker (social/sentiment), " +
|
|
87
|
+
"memory-manager (compress memory), risk-verifier (evaluate risk), " +
|
|
88
|
+
"onchain-analyst (on-chain data: wallets, flows, TVL), " +
|
|
89
|
+
"news-aggregator (latest news + narrative tracking). " +
|
|
88
90
|
"Results are saved to vault automatically.",
|
|
89
91
|
inputSchema: {
|
|
90
92
|
type: "object",
|
|
91
93
|
properties: {
|
|
92
94
|
agentId: {
|
|
93
95
|
type: "string",
|
|
94
|
-
enum: ["market-monitor", "sentiment-tracker", "memory-manager", "risk-verifier", "workflow-executor"],
|
|
96
|
+
enum: ["market-monitor", "sentiment-tracker", "memory-manager", "risk-verifier", "workflow-executor", "onchain-analyst", "news-aggregator"],
|
|
95
97
|
description: "Which agent to run",
|
|
96
98
|
},
|
|
97
99
|
params: {
|
|
98
100
|
type: "object",
|
|
99
|
-
description: "Agent-specific params. market-monitor: { token: 'BTC' }. sentiment-tracker: { token: 'ETH' } or { topic: 'Layer 2s' }.
|
|
101
|
+
description: "Agent-specific params. market-monitor: { token: 'BTC' }. sentiment-tracker: { token: 'ETH' } or { topic: 'Layer 2s' }. onchain-analyst: { address: '0x...' } or { protocol: 'Morpho' }. news-aggregator: { topic: 'Base ecosystem' }.",
|
|
100
102
|
},
|
|
101
103
|
},
|
|
102
104
|
required: ["agentId"],
|
|
@@ -115,6 +117,71 @@ exports.SWARM_TOOLS = [
|
|
|
115
117
|
required: [],
|
|
116
118
|
},
|
|
117
119
|
},
|
|
120
|
+
{
|
|
121
|
+
name: "swarm_broadcast",
|
|
122
|
+
description: "Broadcast a message or signal to all active swarm agents simultaneously. " +
|
|
123
|
+
"All agents will receive and act on the message in their next cycle. " +
|
|
124
|
+
"Use to coordinate the swarm: change focus, alert about market conditions, or inject a directive.",
|
|
125
|
+
inputSchema: {
|
|
126
|
+
type: "object",
|
|
127
|
+
properties: {
|
|
128
|
+
message: { type: "string", description: "Message to broadcast to all agents (max 500 chars)" },
|
|
129
|
+
priority: { type: "string", enum: ["low", "normal", "high", "urgent"], description: "Message priority (default: normal)" },
|
|
130
|
+
targetAgents: { type: "array", items: { type: "string" }, description: "Optional: target specific agent IDs. Omit to broadcast to all." },
|
|
131
|
+
},
|
|
132
|
+
required: ["message"],
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "swarm_pulse",
|
|
137
|
+
description: "Get an instant snapshot from all swarm agents — market prices, sentiment, on-chain activity, and agent health. " +
|
|
138
|
+
"Unlike get_swarm_status, swarm_pulse triggers all agents to report their latest readings right now. " +
|
|
139
|
+
"Best for a quick market briefing or sanity check before making decisions.",
|
|
140
|
+
inputSchema: {
|
|
141
|
+
type: "object",
|
|
142
|
+
properties: {
|
|
143
|
+
token: { type: "string", description: "Optional: focus the pulse on a specific token (e.g. 'BTC', 'ETH')" },
|
|
144
|
+
},
|
|
145
|
+
required: [],
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: "swarm_reflect",
|
|
150
|
+
description: "Consolidate everything the swarm has learned into a single coherent intelligence summary. " +
|
|
151
|
+
"Reads all recent research vault entries from swarm agents, groups by agent, extracts key signals, " +
|
|
152
|
+
"and saves a unified reflection to vault. " +
|
|
153
|
+
"Run this after swarm_research or swarm_pulse to crystallize findings into long-term memory. " +
|
|
154
|
+
"Best used once per research session or daily for ongoing monitoring.",
|
|
155
|
+
inputSchema: {
|
|
156
|
+
type: "object",
|
|
157
|
+
properties: {
|
|
158
|
+
hoursBack: { type: "number", description: "How many hours of swarm activity to include (default: 24)" },
|
|
159
|
+
focus: { type: "string", description: "Optional: focus reflection on a specific topic or token" },
|
|
160
|
+
},
|
|
161
|
+
required: [],
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: "swarm_watch",
|
|
166
|
+
description: "Register a topic or token for continuous swarm monitoring. " +
|
|
167
|
+
"Swarm agents will prioritize this topic in every cycle — price changes, sentiment shifts, news, on-chain flows. " +
|
|
168
|
+
"Findings are saved to vault and semantic memory automatically. " +
|
|
169
|
+
"Use memory_insight or swarm_brief to check accumulated findings. " +
|
|
170
|
+
"Alert conditions: price_spike | sentiment_shift | news | whale_move | all.",
|
|
171
|
+
inputSchema: {
|
|
172
|
+
type: "object",
|
|
173
|
+
properties: {
|
|
174
|
+
topic: { type: "string", description: "What to watch — token symbol, protocol name, or any topic (e.g. 'ETH', 'Lido', 'Base ecosystem')" },
|
|
175
|
+
priority: { type: "string", enum: ["low", "normal", "high"], description: "Monitoring priority (default: normal)" },
|
|
176
|
+
alertOn: {
|
|
177
|
+
type: "array",
|
|
178
|
+
items: { type: "string", enum: ["price_spike", "sentiment_shift", "news", "whale_move", "all"] },
|
|
179
|
+
description: "Which signals to watch for (default: price_spike, sentiment_shift, news)",
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
required: ["topic"],
|
|
183
|
+
},
|
|
184
|
+
},
|
|
118
185
|
];
|
|
119
186
|
const StartSwarmSchema = zod_1.z.object({
|
|
120
187
|
config: zod_1.z.object({
|
|
@@ -127,9 +194,21 @@ const GetMemorySchema = zod_1.z.object({ key: zod_1.z.string().min(1) });
|
|
|
127
194
|
const ResearchSchema = zod_1.z.object({ topic: zod_1.z.string().min(1), depth: zod_1.z.enum(["quick", "standard", "deep"]).optional() });
|
|
128
195
|
const BriefSchema = zod_1.z.object({ limit: zod_1.z.number().optional() });
|
|
129
196
|
const TriggerAgentSchema = zod_1.z.object({
|
|
130
|
-
agentId: zod_1.z.enum(["market-monitor", "sentiment-tracker", "memory-manager", "risk-verifier", "workflow-executor"]),
|
|
197
|
+
agentId: zod_1.z.enum(["market-monitor", "sentiment-tracker", "memory-manager", "risk-verifier", "workflow-executor", "onchain-analyst", "news-aggregator"]),
|
|
131
198
|
params: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(),
|
|
132
199
|
});
|
|
200
|
+
const BroadcastSchema = zod_1.z.object({
|
|
201
|
+
message: zod_1.z.string().min(1).max(500),
|
|
202
|
+
priority: zod_1.z.enum(["low", "normal", "high", "urgent"]).optional(),
|
|
203
|
+
targetAgents: zod_1.z.array(zod_1.z.string()).optional(),
|
|
204
|
+
});
|
|
205
|
+
const PulseSchema = zod_1.z.object({ token: zod_1.z.string().optional() });
|
|
206
|
+
const ReflectSchema = zod_1.z.object({ hoursBack: zod_1.z.number().positive().optional(), focus: zod_1.z.string().optional() });
|
|
207
|
+
const WatchSchema = zod_1.z.object({
|
|
208
|
+
topic: zod_1.z.string().min(1),
|
|
209
|
+
priority: zod_1.z.enum(["low", "normal", "high"]).optional(),
|
|
210
|
+
alertOn: zod_1.z.array(zod_1.z.enum(["price_spike", "sentiment_shift", "news", "whale_move", "all"])).optional(),
|
|
211
|
+
});
|
|
133
212
|
const NO_KEY_MSG = `🔑 Swarm tools require a NoelClaw API key.\n\n` +
|
|
134
213
|
`→ Get yours free at: https://noelclaw.com\n\n` +
|
|
135
214
|
`Then add it to your MCP config:\n` +
|
|
@@ -272,6 +351,9 @@ async function handleSwarmTool(name, args) {
|
|
|
272
351
|
if (!parsed.success)
|
|
273
352
|
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
274
353
|
const { topic, depth = "standard" } = parsed.data;
|
|
354
|
+
// Check what's already known before launching — show value immediately
|
|
355
|
+
const [priorMem] = await Promise.allSettled([(0, memory_js_1.searchSupermemory)(topic, 4)]);
|
|
356
|
+
const priorResults = priorMem.status === "fulfilled" ? priorMem.value : [];
|
|
275
357
|
await (0, convex_js_1.callConvex)("/swarm/start", "POST", {}, "start_swarm").catch(() => { });
|
|
276
358
|
let data;
|
|
277
359
|
try {
|
|
@@ -282,17 +364,26 @@ async function handleSwarmTool(name, args) {
|
|
|
282
364
|
}
|
|
283
365
|
if (data.error)
|
|
284
366
|
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
367
|
+
const priorSection = priorResults.length > 0
|
|
368
|
+
? [
|
|
369
|
+
``,
|
|
370
|
+
`**Prior knowledge loaded (${priorResults.length} memories):**`,
|
|
371
|
+
...priorResults.map(r => `• ${r.metadata?.title ?? r.content.slice(0, 80).replace(/\n/g, " ")}`),
|
|
372
|
+
`Agents will build on this — no re-discovering what you already know.`,
|
|
373
|
+
]
|
|
374
|
+
: [``, `No prior knowledge found — agents starting fresh.`];
|
|
285
375
|
return {
|
|
286
376
|
content: [{
|
|
287
377
|
type: "text",
|
|
288
378
|
text: [
|
|
289
379
|
`🔬 **Swarm Research Started**`,
|
|
290
|
-
`Topic: ${topic}`,
|
|
291
|
-
|
|
380
|
+
`Topic: ${topic} · Depth: ${depth}`,
|
|
381
|
+
...priorSection,
|
|
292
382
|
``,
|
|
293
|
-
data.message ?? "Research triggered. Findings will appear in vault.",
|
|
383
|
+
data.message ?? "Research triggered. Findings will appear in vault automatically.",
|
|
294
384
|
``,
|
|
295
|
-
|
|
385
|
+
`**Next:** \`swarm_reflect focus: "${topic}"\` to consolidate findings`,
|
|
386
|
+
`Or: \`memory_insight topic: "${topic}"\` to see full intelligence report`,
|
|
296
387
|
].join("\n"),
|
|
297
388
|
}],
|
|
298
389
|
};
|
|
@@ -303,9 +394,16 @@ async function handleSwarmTool(name, args) {
|
|
|
303
394
|
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
304
395
|
const { agentId, params = {} } = parsed.data;
|
|
305
396
|
await (0, convex_js_1.callConvex)("/swarm/start", "POST", {}, "start_swarm").catch(() => { });
|
|
397
|
+
// Auto-load prior context so agent doesn't re-discover what's already known
|
|
398
|
+
const contextQuery = [agentId, params?.token, params?.topic, params?.protocol].filter(Boolean).join(" ");
|
|
399
|
+
const priorContext = await (0, memory_js_1.searchSupermemory)(contextQuery, 3);
|
|
400
|
+
const priorSummary = priorContext.length > 0
|
|
401
|
+
? priorContext.map(r => r.content.slice(0, 150)).join(" | ")
|
|
402
|
+
: null;
|
|
403
|
+
const enrichedParams = priorSummary ? { ...params, priorContext: priorSummary } : params;
|
|
306
404
|
let data;
|
|
307
405
|
try {
|
|
308
|
-
data = await (0, convex_js_1.callConvex)("/swarm/trigger", "POST", { agentId, params }, "trigger_agent");
|
|
406
|
+
data = await (0, convex_js_1.callConvex)("/swarm/trigger", "POST", { agentId, params: enrichedParams }, "trigger_agent");
|
|
309
407
|
}
|
|
310
408
|
catch (err) {
|
|
311
409
|
return swarmAuthError(err);
|
|
@@ -313,15 +411,17 @@ async function handleSwarmTool(name, args) {
|
|
|
313
411
|
if (data.error)
|
|
314
412
|
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
315
413
|
const resultText = data.result ? `\n\`\`\`json\n${JSON.stringify(data.result, null, 2).slice(0, 800)}\n\`\`\`` : "";
|
|
414
|
+
const contextNote = priorContext.length > 0 ? `🧠 ${priorContext.length} prior memory entries injected into agent context.` : "";
|
|
316
415
|
return {
|
|
317
416
|
content: [{
|
|
318
417
|
type: "text",
|
|
319
418
|
text: [
|
|
320
419
|
`⚡ **${agentId} triggered**`,
|
|
321
|
-
|
|
420
|
+
contextNote,
|
|
322
421
|
resultText,
|
|
323
422
|
``,
|
|
324
|
-
`
|
|
423
|
+
`Findings saved to vault automatically.`,
|
|
424
|
+
`Use \`swarm_reflect\` to consolidate or \`memory_insight topic: "${contextQuery.split(" ")[1] ?? agentId}"\` for full report.`,
|
|
325
425
|
].filter(Boolean).join("\n"),
|
|
326
426
|
}],
|
|
327
427
|
};
|
|
@@ -350,9 +450,222 @@ async function handleSwarmTool(name, args) {
|
|
|
350
450
|
lines.push(`**[${e.agentId}]** ${e.title}`);
|
|
351
451
|
lines.push(` _${e.key}_ · v${e.version} · ${formatDate(e.updatedAt)}`);
|
|
352
452
|
}
|
|
353
|
-
lines.push(`\nUse \`
|
|
453
|
+
lines.push(`\nUse \`memory_context topic: "<topic>"\` to load full content for any research area.`);
|
|
454
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
455
|
+
}
|
|
456
|
+
case "swarm_broadcast": {
|
|
457
|
+
const parsed = BroadcastSchema.safeParse(args);
|
|
458
|
+
if (!parsed.success)
|
|
459
|
+
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
460
|
+
const { message, priority = "normal", targetAgents } = parsed.data;
|
|
461
|
+
let data;
|
|
462
|
+
try {
|
|
463
|
+
data = await (0, convex_js_1.callConvex)("/swarm/broadcast", "POST", { message, priority, targetAgents }, "swarm_broadcast");
|
|
464
|
+
}
|
|
465
|
+
catch (err) {
|
|
466
|
+
return swarmAuthError(err);
|
|
467
|
+
}
|
|
468
|
+
if (data.error)
|
|
469
|
+
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
470
|
+
const targets = targetAgents?.join(", ") ?? "all agents";
|
|
471
|
+
return {
|
|
472
|
+
content: [{
|
|
473
|
+
type: "text",
|
|
474
|
+
text: [
|
|
475
|
+
`📡 **Broadcast sent** [${priority}]`,
|
|
476
|
+
`To: ${targets}`,
|
|
477
|
+
`Message: "${message}"`,
|
|
478
|
+
data.deliveredTo ? `Delivered to ${data.deliveredTo} agent(s)` : "",
|
|
479
|
+
].filter(Boolean).join("\n"),
|
|
480
|
+
}],
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
case "swarm_pulse": {
|
|
484
|
+
const parsed = PulseSchema.safeParse(args ?? {});
|
|
485
|
+
if (!parsed.success)
|
|
486
|
+
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
487
|
+
const { token } = parsed.data;
|
|
488
|
+
await (0, convex_js_1.callConvex)("/swarm/start", "POST", {}, "start_swarm").catch(() => { });
|
|
489
|
+
let data;
|
|
490
|
+
try {
|
|
491
|
+
data = await (0, convex_js_1.callConvex)("/swarm/pulse", "POST", { token }, "swarm_pulse");
|
|
492
|
+
}
|
|
493
|
+
catch (err) {
|
|
494
|
+
return swarmAuthError(err);
|
|
495
|
+
}
|
|
496
|
+
if (data.error)
|
|
497
|
+
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
498
|
+
const readings = data.readings ?? [];
|
|
499
|
+
const lines = [
|
|
500
|
+
`💓 **Swarm Pulse**${token ? ` — ${token}` : ""}`,
|
|
501
|
+
`Agents reporting: ${readings.length}`,
|
|
502
|
+
``,
|
|
503
|
+
];
|
|
504
|
+
for (const r of readings) {
|
|
505
|
+
lines.push(`**[${r.agentId}]** ${r.summary ?? "No data"}`);
|
|
506
|
+
if (r.data) {
|
|
507
|
+
const preview = JSON.stringify(r.data).slice(0, 120);
|
|
508
|
+
lines.push(` ${preview}${preview.length === 120 ? "…" : ""}`);
|
|
509
|
+
}
|
|
510
|
+
lines.push("");
|
|
511
|
+
}
|
|
512
|
+
if (!readings.length)
|
|
513
|
+
lines.push("No agents responding. Use `start_swarm` first.");
|
|
354
514
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
355
515
|
}
|
|
516
|
+
case "swarm_reflect": {
|
|
517
|
+
const parsed = ReflectSchema.safeParse(args ?? {});
|
|
518
|
+
if (!parsed.success)
|
|
519
|
+
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
520
|
+
const { hoursBack = 24, focus } = parsed.data;
|
|
521
|
+
const params = new URLSearchParams({ type: "research", limit: "30" });
|
|
522
|
+
let data;
|
|
523
|
+
try {
|
|
524
|
+
data = await (0, convex_js_1.callConvex)(`/vault/list?${params}`, "GET", undefined, "swarm_reflect");
|
|
525
|
+
}
|
|
526
|
+
catch (err) {
|
|
527
|
+
return swarmAuthError(err);
|
|
528
|
+
}
|
|
529
|
+
if (data.error)
|
|
530
|
+
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
531
|
+
const SWARM_AGENTS = new Set(["market-monitor", "sentiment-tracker", "onchain-analyst", "news-aggregator", "risk-verifier", "memory-manager"]);
|
|
532
|
+
const cutoff = Date.now() - hoursBack * 3600000;
|
|
533
|
+
const entries = (data.entries ?? []).filter((e) => {
|
|
534
|
+
const isSwarm = SWARM_AGENTS.has(e.agentId);
|
|
535
|
+
const isRecent = e.updatedAt >= cutoff;
|
|
536
|
+
const matchesFocus = !focus || e.title.toLowerCase().includes(focus.toLowerCase());
|
|
537
|
+
return isSwarm && isRecent && matchesFocus;
|
|
538
|
+
});
|
|
539
|
+
if (!entries.length) {
|
|
540
|
+
return {
|
|
541
|
+
content: [{
|
|
542
|
+
type: "text",
|
|
543
|
+
text: [
|
|
544
|
+
`📋 **Swarm Reflection** — Nothing to consolidate`,
|
|
545
|
+
``,
|
|
546
|
+
`No swarm research found in the last ${hoursBack}h${focus ? ` about "${focus}"` : ""}.`,
|
|
547
|
+
``,
|
|
548
|
+
`Start one: \`swarm_research topic: "${focus ?? "market overview"}"\``,
|
|
549
|
+
].join("\n"),
|
|
550
|
+
}],
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
// Group by agent
|
|
554
|
+
const byAgent = {};
|
|
555
|
+
for (const e of entries) {
|
|
556
|
+
const a = e.agentId ?? "unknown";
|
|
557
|
+
(byAgent[a] ?? (byAgent[a] = [])).push(e);
|
|
558
|
+
}
|
|
559
|
+
const agentSummaries = Object.entries(byAgent).map(([agent, es]) => {
|
|
560
|
+
const items = es.map((e) => ` • ${e.title}`).join("\n");
|
|
561
|
+
return `**[${agent}]** — ${es.length} finding(s)\n${items}`;
|
|
562
|
+
});
|
|
563
|
+
const signals = entries
|
|
564
|
+
.sort((a, b) => b.updatedAt - a.updatedAt)
|
|
565
|
+
.slice(0, 6)
|
|
566
|
+
.map((e) => `• [${e.agentId}] ${e.title}`);
|
|
567
|
+
// Write synthesis to vault for long-term memory
|
|
568
|
+
const now = new Date();
|
|
569
|
+
const reflectionKey = `swarm/reflection-${now.toISOString().slice(0, 10)}-${now.getHours()}h`;
|
|
570
|
+
const reflectionContent = [
|
|
571
|
+
`# Swarm Reflection — ${now.toUTCString()}`,
|
|
572
|
+
`Period: last ${hoursBack}h${focus ? ` · Focus: ${focus}` : ""} · ${entries.length} entries from ${Object.keys(byAgent).length} agent(s)`,
|
|
573
|
+
``,
|
|
574
|
+
`## Agent Findings`,
|
|
575
|
+
agentSummaries.join("\n\n"),
|
|
576
|
+
``,
|
|
577
|
+
`## Key Signals`,
|
|
578
|
+
signals.join("\n"),
|
|
579
|
+
].join("\n");
|
|
580
|
+
const saved = await (0, convex_js_1.callConvex)("/vault/save", "POST", {
|
|
581
|
+
type: "research",
|
|
582
|
+
title: `Swarm Reflection — ${now.toLocaleDateString("en-US")}${focus ? ` — ${focus}` : ""}`,
|
|
583
|
+
content: reflectionContent,
|
|
584
|
+
key: reflectionKey,
|
|
585
|
+
agentId: "swarm-coordinator",
|
|
586
|
+
tags: ["reflection", "swarm", ...(focus ? [focus] : [])],
|
|
587
|
+
commitMsg: "swarm_reflect auto-consolidation",
|
|
588
|
+
}, "swarm_reflect").catch(() => null);
|
|
589
|
+
// Also sync to semantic memory so it's retrievable by topic
|
|
590
|
+
if (saved) {
|
|
591
|
+
(0, memory_js_1.syncToSupermemory)(reflectionContent, {
|
|
592
|
+
vaultKey: reflectionKey, title: `Swarm Reflection ${now.toLocaleDateString("en-US")}`,
|
|
593
|
+
type: "research", tags: ["reflection"], source: "swarm_reflect",
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
return {
|
|
597
|
+
content: [{
|
|
598
|
+
type: "text",
|
|
599
|
+
text: [
|
|
600
|
+
`📋 **Swarm Reflection** — ${entries.length} findings from ${Object.keys(byAgent).length} agent(s)`,
|
|
601
|
+
`Period: last ${hoursBack}h${focus ? ` · Focus: ${focus}` : ""}`,
|
|
602
|
+
``,
|
|
603
|
+
agentSummaries.join("\n\n"),
|
|
604
|
+
``,
|
|
605
|
+
`**Key signals:**`,
|
|
606
|
+
...signals,
|
|
607
|
+
``,
|
|
608
|
+
saved ? `✅ Synthesis saved to vault: \`${reflectionKey}\`` : "⚠️ Could not save synthesis",
|
|
609
|
+
``,
|
|
610
|
+
`Use \`memory_insight topic: "${focus ?? "swarm research"}"\` for the full intelligence picture.`,
|
|
611
|
+
].filter(Boolean).join("\n"),
|
|
612
|
+
}],
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
case "swarm_watch": {
|
|
616
|
+
const parsed = WatchSchema.safeParse(args);
|
|
617
|
+
if (!parsed.success)
|
|
618
|
+
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
619
|
+
const { topic, priority = "normal", alertOn = ["price_spike", "sentiment_shift", "news"] } = parsed.data;
|
|
620
|
+
const watchKey = `watch:${topic.toLowerCase().replace(/\s+/g, "-")}`;
|
|
621
|
+
const watchConfig = JSON.stringify({
|
|
622
|
+
topic, priority,
|
|
623
|
+
alertOn: alertOn.includes("all") ? ["price_spike", "sentiment_shift", "news", "whale_move"] : alertOn,
|
|
624
|
+
registeredAt: Date.now(),
|
|
625
|
+
});
|
|
626
|
+
// Write to swarm shared memory (picked up by agents in next cycle)
|
|
627
|
+
await (0, convex_js_1.callConvex)("/swarm/memory/write", "POST", {
|
|
628
|
+
agentId: "swarm-coordinator",
|
|
629
|
+
key: watchKey,
|
|
630
|
+
value: watchConfig,
|
|
631
|
+
}, "swarm_watch").catch(() => { });
|
|
632
|
+
// Persist to vault so it survives swarm restarts
|
|
633
|
+
await (0, convex_js_1.callConvex)("/vault/save", "POST", {
|
|
634
|
+
type: "memory",
|
|
635
|
+
title: `Watch: ${topic}`,
|
|
636
|
+
content: watchConfig,
|
|
637
|
+
key: `watch/${topic.toLowerCase().replace(/\s+/g, "-")}`,
|
|
638
|
+
agentId: "swarm-coordinator",
|
|
639
|
+
tags: ["watch", "monitor", topic.toLowerCase()],
|
|
640
|
+
commitMsg: "swarm_watch registered",
|
|
641
|
+
}, "swarm_watch").catch(() => { });
|
|
642
|
+
const alertList = (alertOn.includes("all")
|
|
643
|
+
? ["price_spike", "sentiment_shift", "news", "whale_move"]
|
|
644
|
+
: alertOn).join(", ");
|
|
645
|
+
return {
|
|
646
|
+
content: [{
|
|
647
|
+
type: "text",
|
|
648
|
+
text: [
|
|
649
|
+
`👁️ **Watch Registered: ${topic}**`,
|
|
650
|
+
`Priority: ${priority} · Alerts: ${alertList}`,
|
|
651
|
+
``,
|
|
652
|
+
`Swarm agents will now prioritize "${topic}" in every monitoring cycle.`,
|
|
653
|
+
`Watch key: \`${watchKey}\``,
|
|
654
|
+
``,
|
|
655
|
+
`**What happens next:**`,
|
|
656
|
+
`• market-monitor will track price & volume anomalies`,
|
|
657
|
+
`• sentiment-tracker will watch social signal shifts`,
|
|
658
|
+
`• news-aggregator will flag relevant news & narratives`,
|
|
659
|
+
alertOn.includes("whale_move") || alertOn.includes("all") ? `• onchain-analyst will detect large wallet movements` : "",
|
|
660
|
+
``,
|
|
661
|
+
`**Check findings:**`,
|
|
662
|
+
`• \`swarm_brief\` — latest research entries`,
|
|
663
|
+
`• \`memory_insight topic: "${topic}"\` — full intelligence report`,
|
|
664
|
+
`• \`swarm_reflect focus: "${topic}"\` — consolidated summary`,
|
|
665
|
+
].filter(Boolean).join("\n"),
|
|
666
|
+
}],
|
|
667
|
+
};
|
|
668
|
+
}
|
|
356
669
|
default:
|
|
357
670
|
return null;
|
|
358
671
|
}
|