@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/memory.js
CHANGED
|
@@ -1,82 +1,54 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MEMORY_TOOLS =
|
|
4
|
-
exports.smFetch = smFetch;
|
|
3
|
+
exports.MEMORY_TOOLS = void 0;
|
|
5
4
|
exports.syncToSupermemory = syncToSupermemory;
|
|
6
5
|
exports.searchSupermemory = searchSupermemory;
|
|
7
6
|
exports.handleMemoryTool = handleMemoryTool;
|
|
8
7
|
const zod_1 = require("zod");
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
// ─── Supermemory HTTP helpers ────────────────────────────────────────────────
|
|
12
|
-
async function smFetch(path, method, body) {
|
|
13
|
-
const apiKey = process.env.SUPERMEMORY_API_KEY;
|
|
14
|
-
if (!apiKey)
|
|
15
|
-
return { ok: false, data: null, error: "SUPERMEMORY_API_KEY not set" };
|
|
16
|
-
try {
|
|
17
|
-
const res = await fetch(`${SM_BASE}${path}`, {
|
|
18
|
-
method,
|
|
19
|
-
headers: {
|
|
20
|
-
Authorization: `Bearer ${apiKey}`,
|
|
21
|
-
"Content-Type": "application/json",
|
|
22
|
-
},
|
|
23
|
-
body: body ? JSON.stringify(body) : undefined,
|
|
24
|
-
signal: AbortSignal.timeout(15000),
|
|
25
|
-
});
|
|
26
|
-
if (!res.ok) {
|
|
27
|
-
const err = await res.text().catch(() => "");
|
|
28
|
-
return { ok: false, data: null, error: `${res.status}: ${err.slice(0, 120)}` };
|
|
29
|
-
}
|
|
30
|
-
return { ok: true, data: await res.json() };
|
|
31
|
-
}
|
|
32
|
-
catch (err) {
|
|
33
|
-
return { ok: false, data: null, error: err.message };
|
|
34
|
-
}
|
|
35
|
-
}
|
|
8
|
+
const convex_js_1 = require("../convex.js");
|
|
9
|
+
// ─── Helpers (proxied through Convex — server-side Supermemory key) ──────────
|
|
36
10
|
async function syncToSupermemory(content, metadata, sourceUrl) {
|
|
37
|
-
await
|
|
11
|
+
await (0, convex_js_1.callConvex)("/memory/add", "POST", {
|
|
38
12
|
content,
|
|
39
13
|
metadata,
|
|
40
|
-
spaces: [exports.SM_SPACE],
|
|
41
14
|
...(sourceUrl ? { sourceUrl } : {}),
|
|
42
|
-
});
|
|
15
|
+
}).catch(() => { });
|
|
43
16
|
}
|
|
44
17
|
async function searchSupermemory(query, limit = 10) {
|
|
45
|
-
|
|
46
|
-
q: query,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
});
|
|
51
|
-
if (!ok || !data?.results)
|
|
18
|
+
try {
|
|
19
|
+
const data = await (0, convex_js_1.callConvex)("/memory/search", "POST", { q: query, n: limit });
|
|
20
|
+
return data?.results ?? [];
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
52
23
|
return [];
|
|
53
|
-
|
|
24
|
+
}
|
|
54
25
|
}
|
|
55
26
|
// ─── Tool definitions ────────────────────────────────────────────────────────
|
|
56
27
|
exports.MEMORY_TOOLS = [
|
|
57
28
|
{
|
|
58
29
|
name: "memory_add",
|
|
59
|
-
description: "Add content to Noelclaw
|
|
60
|
-
"Unlike vault_save, memory_add is
|
|
61
|
-
"Use
|
|
62
|
-
"
|
|
63
|
-
"'what did I say about ETH yield?'
|
|
30
|
+
description: "Add content to your Noelclaw semantic memory — no setup needed, no extra API keys. " +
|
|
31
|
+
"Unlike vault_save, memory_add is instant: no versioning, no type required. " +
|
|
32
|
+
"Use for notes, decisions, preferences, or anything you want to find later with natural language. " +
|
|
33
|
+
"Pass sourceUrl to fetch and index any web page, GitHub repo, or Notion page automatically — " +
|
|
34
|
+
"searchable in ~30s. Memory is indexed semantically — 'what did I say about ETH yield?' " +
|
|
35
|
+
"will find it even without exact keywords.",
|
|
64
36
|
inputSchema: {
|
|
65
37
|
type: "object",
|
|
66
38
|
properties: {
|
|
67
|
-
content: { type: "string", description: "Content to remember — text, markdown, or a note" },
|
|
39
|
+
content: { type: "string", description: "Content to remember — text, markdown, or a note. Use a short title if providing sourceUrl." },
|
|
68
40
|
title: { type: "string", description: "Optional title for this memory" },
|
|
69
41
|
tags: { type: "array", items: { type: "string" }, description: "Tags for grouping" },
|
|
70
|
-
sourceUrl: { type: "string", description: "
|
|
42
|
+
sourceUrl: { type: "string", description: "URL to fetch and index automatically (GitHub, Notion, web page, etc.). Content becomes searchable in ~30s." },
|
|
71
43
|
},
|
|
72
44
|
required: ["content"],
|
|
73
45
|
},
|
|
74
46
|
},
|
|
75
47
|
{
|
|
76
48
|
name: "memory_search",
|
|
77
|
-
description: "Semantic search across all Noelclaw memories.
|
|
78
|
-
"
|
|
79
|
-
"Also searches memories synced from vault_save
|
|
49
|
+
description: "Semantic search across all your Noelclaw memories. Understands meaning, not just keywords — " +
|
|
50
|
+
"'low risk crypto yield' matches 'conservative DeFi strategies'. " +
|
|
51
|
+
"Also searches memories auto-synced from vault_save.",
|
|
80
52
|
inputSchema: {
|
|
81
53
|
type: "object",
|
|
82
54
|
properties: {
|
|
@@ -88,10 +60,9 @@ exports.MEMORY_TOOLS = [
|
|
|
88
60
|
},
|
|
89
61
|
{
|
|
90
62
|
name: "memory_context",
|
|
91
|
-
description: "Retrieve the most semantically relevant memories for a topic
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"not just keyword matching.",
|
|
63
|
+
description: "Retrieve the most semantically relevant memories for a topic, formatted as AI-ready context. " +
|
|
64
|
+
"Use at the start of research tasks to prime with everything stored about a topic. " +
|
|
65
|
+
"Uses vector search — finds semantically related content, not just exact keyword matches.",
|
|
95
66
|
inputSchema: {
|
|
96
67
|
type: "object",
|
|
97
68
|
properties: {
|
|
@@ -103,8 +74,8 @@ exports.MEMORY_TOOLS = [
|
|
|
103
74
|
},
|
|
104
75
|
{
|
|
105
76
|
name: "memory_profile",
|
|
106
|
-
description: "Show your semantic memory stats —
|
|
107
|
-
"
|
|
77
|
+
description: "Show your semantic memory stats — total memories stored, your memory space, and connected sources. " +
|
|
78
|
+
"Useful for auditing what Noelclaw knows about you.",
|
|
108
79
|
inputSchema: {
|
|
109
80
|
type: "object",
|
|
110
81
|
properties: {},
|
|
@@ -112,33 +83,44 @@ exports.MEMORY_TOOLS = [
|
|
|
112
83
|
},
|
|
113
84
|
},
|
|
114
85
|
{
|
|
115
|
-
name: "
|
|
116
|
-
description: "
|
|
117
|
-
"
|
|
118
|
-
"
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
86
|
+
name: "memory_list",
|
|
87
|
+
description: "List your most recent Noelclaw memories without a search query. " +
|
|
88
|
+
"Useful to browse what's stored or audit before clearing. " +
|
|
89
|
+
"Sorted by most recently added.",
|
|
90
|
+
inputSchema: {
|
|
91
|
+
type: "object",
|
|
92
|
+
properties: {
|
|
93
|
+
limit: { type: "number", description: "Max memories to return (default 20)" },
|
|
94
|
+
tag: { type: "string", description: "Optional: filter by tag" },
|
|
95
|
+
},
|
|
96
|
+
required: [],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: "memory_delete",
|
|
101
|
+
description: "Delete a specific memory by its ID. Get IDs from memory_search or memory_list results. " +
|
|
102
|
+
"This permanently removes the memory from your semantic store.",
|
|
123
103
|
inputSchema: {
|
|
124
104
|
type: "object",
|
|
125
105
|
properties: {
|
|
126
|
-
|
|
127
|
-
type: "string",
|
|
128
|
-
enum: ["url", "github", "notion", "manual"],
|
|
129
|
-
description: "Source type",
|
|
130
|
-
},
|
|
131
|
-
url: {
|
|
132
|
-
type: "string",
|
|
133
|
-
description: "URL to fetch and index (required for url/github/notion sources)",
|
|
134
|
-
},
|
|
135
|
-
content: {
|
|
136
|
-
type: "string",
|
|
137
|
-
description: "Direct content (for source: manual)",
|
|
138
|
-
},
|
|
139
|
-
title: { type: "string", description: "Title for this memory" },
|
|
106
|
+
id: { type: "string", description: "Memory ID to delete (from memory_search or memory_list results)" },
|
|
140
107
|
},
|
|
141
|
-
required: ["
|
|
108
|
+
required: ["id"],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "memory_insight",
|
|
113
|
+
description: "Get a full intelligence report on any topic — combines semantic memory AND vault entries, " +
|
|
114
|
+
"then identifies knowledge gaps and suggests next actions. " +
|
|
115
|
+
"Use this before starting any research or trade decision to see everything Noelclaw already knows. " +
|
|
116
|
+
"Returns: confidence level, what you know, coverage timeline, gaps, and recommended next steps.",
|
|
117
|
+
inputSchema: {
|
|
118
|
+
type: "object",
|
|
119
|
+
properties: {
|
|
120
|
+
topic: { type: "string", description: "Topic to analyze — token, protocol, strategy, or any concept" },
|
|
121
|
+
depth: { type: "string", enum: ["quick", "standard", "deep"], description: "How many sources to pull (default: standard)" },
|
|
122
|
+
},
|
|
123
|
+
required: ["topic"],
|
|
142
124
|
},
|
|
143
125
|
},
|
|
144
126
|
];
|
|
@@ -157,42 +139,40 @@ const ContextSchema = zod_1.z.object({
|
|
|
157
139
|
topic: zod_1.z.string().min(1),
|
|
158
140
|
limit: zod_1.z.number().optional(),
|
|
159
141
|
});
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
142
|
+
const ListSchema = zod_1.z.object({
|
|
143
|
+
limit: zod_1.z.number().optional(),
|
|
144
|
+
tag: zod_1.z.string().optional(),
|
|
145
|
+
});
|
|
146
|
+
const DeleteMemSchema = zod_1.z.object({ id: zod_1.z.string().min(1) });
|
|
147
|
+
const InsightSchema = zod_1.z.object({
|
|
148
|
+
topic: zod_1.z.string().min(1),
|
|
149
|
+
depth: zod_1.z.enum(["quick", "standard", "deep"]).optional(),
|
|
165
150
|
});
|
|
166
151
|
// ─── Handler ─────────────────────────────────────────────────────────────────
|
|
167
152
|
async function handleMemoryTool(name, args) {
|
|
168
|
-
const apiKey = process.env.SUPERMEMORY_API_KEY;
|
|
169
153
|
switch (name) {
|
|
170
154
|
case "memory_add": {
|
|
171
155
|
const parsed = AddSchema.safeParse(args);
|
|
172
156
|
if (!parsed.success)
|
|
173
157
|
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
174
|
-
if (!apiKey)
|
|
175
|
-
return { content: [{ type: "text", text: "⚠️ SUPERMEMORY_API_KEY not configured.\nSet it in your MCP env to enable semantic memory.\nGet a free key at supermemory.ai" }], isError: true };
|
|
176
158
|
const { content, title, tags, sourceUrl } = parsed.data;
|
|
177
|
-
const
|
|
159
|
+
const data = await (0, convex_js_1.callConvex)("/memory/add", "POST", {
|
|
178
160
|
content,
|
|
179
|
-
metadata: { title, tags, source: "
|
|
180
|
-
spaces: [exports.SM_SPACE],
|
|
161
|
+
metadata: { title, tags, source: "memory_add", addedAt: Date.now() },
|
|
181
162
|
...(sourceUrl ? { sourceUrl } : {}),
|
|
182
|
-
});
|
|
183
|
-
if (
|
|
184
|
-
return { content: [{ type: "text", text: `Error
|
|
185
|
-
const memId = data?.id ?? data?.memoryId ?? "saved";
|
|
163
|
+
}).catch((err) => ({ error: err.message }));
|
|
164
|
+
if (data?.error)
|
|
165
|
+
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
186
166
|
return {
|
|
187
167
|
content: [{
|
|
188
168
|
type: "text",
|
|
189
169
|
text: [
|
|
190
|
-
`🧠 **Memory added** — ID: \`${
|
|
170
|
+
`🧠 **Memory added** — ID: \`${data?.id ?? "saved"}\``,
|
|
191
171
|
title ? `Title: ${title}` : "",
|
|
192
|
-
sourceUrl ? `Source: ${sourceUrl}` : "",
|
|
172
|
+
sourceUrl ? `Source: ${sourceUrl} (indexing in background…)` : "",
|
|
193
173
|
tags?.length ? `Tags: ${tags.join(", ")}` : "",
|
|
194
174
|
``,
|
|
195
|
-
`Find it
|
|
175
|
+
`Find it with: \`memory_search query: "${(title ?? content).slice(0, 40)}"\``,
|
|
196
176
|
].filter(Boolean).join("\n"),
|
|
197
177
|
}],
|
|
198
178
|
};
|
|
@@ -201,8 +181,6 @@ async function handleMemoryTool(name, args) {
|
|
|
201
181
|
const parsed = SearchSchema.safeParse(args);
|
|
202
182
|
if (!parsed.success)
|
|
203
183
|
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
204
|
-
if (!apiKey)
|
|
205
|
-
return { content: [{ type: "text", text: "⚠️ SUPERMEMORY_API_KEY not configured. Set it in your MCP env to enable semantic memory." }], isError: true };
|
|
206
184
|
const { query, limit = 10 } = parsed.data;
|
|
207
185
|
const results = await searchSupermemory(query, limit);
|
|
208
186
|
if (!results.length)
|
|
@@ -223,12 +201,10 @@ async function handleMemoryTool(name, args) {
|
|
|
223
201
|
const parsed = ContextSchema.safeParse(args);
|
|
224
202
|
if (!parsed.success)
|
|
225
203
|
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
226
|
-
if (!apiKey)
|
|
227
|
-
return { content: [{ type: "text", text: "⚠️ SUPERMEMORY_API_KEY not configured. Set it in your MCP env to enable semantic memory." }], isError: true };
|
|
228
204
|
const { topic, limit = 8 } = parsed.data;
|
|
229
205
|
const results = await searchSupermemory(topic, limit);
|
|
230
206
|
if (!results.length)
|
|
231
|
-
return { content: [{ type: "text", text: `No semantic context found for: "${topic}"\nBuild your memory base with vault_save
|
|
207
|
+
return { content: [{ type: "text", text: `No semantic context found for: "${topic}"\nBuild your memory base with vault_save or memory_add.` }] };
|
|
232
208
|
const contextParts = results.map((r, i) => {
|
|
233
209
|
const title = r.metadata?.title ? `### ${r.metadata.title}` : `### Memory ${i + 1}`;
|
|
234
210
|
return `${title}\n${r.content}`;
|
|
@@ -249,89 +225,141 @@ async function handleMemoryTool(name, args) {
|
|
|
249
225
|
};
|
|
250
226
|
}
|
|
251
227
|
case "memory_profile": {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
return { content: [{ type: "text", text: `Error fetching profile: ${error}` }], isError: true };
|
|
257
|
-
const count = data?.total ?? data?.count ?? "unknown";
|
|
228
|
+
const data = await (0, convex_js_1.callConvex)("/memory/profile", "GET").catch(() => null);
|
|
229
|
+
const total = data?.total ?? 0;
|
|
230
|
+
const status = data?.status ?? "unknown";
|
|
231
|
+
const space = data?.space ?? "—";
|
|
258
232
|
return {
|
|
259
233
|
content: [{
|
|
260
234
|
type: "text",
|
|
261
235
|
text: [
|
|
262
|
-
`🧠 **Noelclaw Semantic Memory
|
|
236
|
+
`🧠 **Noelclaw Semantic Memory**`,
|
|
263
237
|
``,
|
|
264
|
-
`Space: \`${
|
|
265
|
-
`Total memories: **${
|
|
266
|
-
`Status: ✅
|
|
238
|
+
`Space: \`${space}\``,
|
|
239
|
+
`Total memories: **${total}**`,
|
|
240
|
+
`Status: ${status === "ok" ? "✅ Active" : status === "not_configured" ? "⏳ Setting up" : "⚠️ " + status}`,
|
|
267
241
|
``,
|
|
268
|
-
`**
|
|
269
|
-
`• vault_save
|
|
270
|
-
`• memory_add
|
|
271
|
-
`•
|
|
272
|
-
`• Google Drive / Gmail / Notion — connect via noelclaw.com dashboard`,
|
|
242
|
+
`**Auto-synced sources:**`,
|
|
243
|
+
`• vault_save — ✅`,
|
|
244
|
+
`• memory_add (URL indexing) — ✅`,
|
|
245
|
+
`• Google Drive / Gmail / Notion — connect at noelclaw.com`,
|
|
273
246
|
``,
|
|
274
|
-
`**Capabilities
|
|
275
|
-
`• Semantic search (vector similarity)`,
|
|
276
|
-
`• 81.6% LongMemEval accuracy`,
|
|
277
|
-
`• Context injection for AI tasks`,
|
|
247
|
+
`**Capabilities:** Semantic search · Vector context · 81.6% LongMemEval`,
|
|
278
248
|
].join("\n"),
|
|
279
249
|
}],
|
|
280
250
|
};
|
|
281
251
|
}
|
|
282
|
-
case "
|
|
283
|
-
const parsed =
|
|
252
|
+
case "memory_list": {
|
|
253
|
+
const parsed = ListSchema.safeParse(args ?? {});
|
|
284
254
|
if (!parsed.success)
|
|
285
255
|
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
256
|
+
const { limit = 20, tag } = parsed.data;
|
|
257
|
+
const data = await (0, convex_js_1.callConvex)("/memory/list", "POST", { n: limit, tag }).catch((err) => ({ error: err.message }));
|
|
258
|
+
if (data?.error)
|
|
259
|
+
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
260
|
+
const results = data?.results ?? [];
|
|
261
|
+
if (!results.length)
|
|
262
|
+
return { content: [{ type: "text", text: `No memories stored yet. Use \`memory_add\` to start building your knowledge base.` }] };
|
|
263
|
+
const header = `🧠 **Memories** (${results.length} shown${tag ? `, tag: ${tag}` : ""})`;
|
|
264
|
+
const rows = results.map((r, i) => {
|
|
265
|
+
const title = r.metadata?.title ?? "";
|
|
266
|
+
const preview = r.content.slice(0, 100).replace(/\n/g, " ");
|
|
267
|
+
return `${i + 1}. \`${r.id}\`${title ? ` **${title}**` : ""}\n ${preview}${r.content.length > 100 ? "…" : ""}`;
|
|
268
|
+
});
|
|
269
|
+
return { content: [{ type: "text", text: [header, "", ...rows].join("\n") }] };
|
|
270
|
+
}
|
|
271
|
+
case "memory_delete": {
|
|
272
|
+
const parsed = DeleteMemSchema.safeParse(args);
|
|
273
|
+
if (!parsed.success)
|
|
274
|
+
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
275
|
+
const data = await (0, convex_js_1.callConvex)("/memory/delete", "POST", { id: parsed.data.id }).catch((err) => ({ error: err.message }));
|
|
276
|
+
if (data?.error)
|
|
277
|
+
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
278
|
+
return { content: [{ type: "text", text: `🗑️ Memory deleted: \`${parsed.data.id}\`` }] };
|
|
279
|
+
}
|
|
280
|
+
case "memory_insight": {
|
|
281
|
+
const parsed = InsightSchema.safeParse(args);
|
|
282
|
+
if (!parsed.success)
|
|
283
|
+
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
284
|
+
const { topic, depth = "standard" } = parsed.data;
|
|
285
|
+
const memLimit = depth === "deep" ? 15 : depth === "quick" ? 5 : 8;
|
|
286
|
+
// Pull from semantic memory and vault in parallel
|
|
287
|
+
const [memResults, vaultData] = await Promise.all([
|
|
288
|
+
searchSupermemory(topic, memLimit),
|
|
289
|
+
(0, convex_js_1.callConvex)(`/vault/search?q=${encodeURIComponent(topic)}&limit=6`, "GET", undefined, "memory_insight").catch(() => ({ results: [] })),
|
|
290
|
+
]);
|
|
291
|
+
const vaultResults = vaultData.results ?? [];
|
|
292
|
+
const total = memResults.length + vaultResults.length;
|
|
293
|
+
if (!total) {
|
|
294
|
+
return {
|
|
295
|
+
content: [{
|
|
296
|
+
type: "text",
|
|
297
|
+
text: [
|
|
298
|
+
`🔮 **Intelligence Report: "${topic}"**`,
|
|
299
|
+
``,
|
|
300
|
+
`No knowledge found yet.`,
|
|
301
|
+
``,
|
|
302
|
+
`**Start building:**`,
|
|
303
|
+
`• \`swarm_research topic: "${topic}"\` — run deep research now`,
|
|
304
|
+
`• \`memory_add content: "..." \` — add a manual note`,
|
|
305
|
+
`• \`swarm_watch topic: "${topic}"\` — start monitoring`,
|
|
306
|
+
].join("\n"),
|
|
307
|
+
}],
|
|
308
|
+
};
|
|
300
309
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
310
|
+
// Confidence tier
|
|
311
|
+
const confidence = total >= 10 ? "🟢 High" : total >= 4 ? "🟡 Medium" : "🔴 Low";
|
|
312
|
+
// Timeline from metadata timestamps
|
|
313
|
+
const timestamps = memResults.map(r => r.metadata?.addedAt).filter(Boolean);
|
|
314
|
+
const oldest = timestamps.length ? Math.min(...timestamps) : null;
|
|
315
|
+
const newest = timestamps.length ? Math.max(...timestamps) : null;
|
|
316
|
+
const daysSinceUpdate = newest ? Math.round((Date.now() - newest) / 86400000) : null;
|
|
317
|
+
// Knowledge summary lines
|
|
318
|
+
const memLines = memResults.slice(0, 6).map(r => {
|
|
319
|
+
const title = r.metadata?.title ?? r.content.slice(0, 70).replace(/\n/g, " ");
|
|
320
|
+
const score = r.score != null ? ` [${(r.score * 100).toFixed(0)}%]` : "";
|
|
321
|
+
return ` •${score} ${title}`;
|
|
308
322
|
});
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
323
|
+
const vaultLines = vaultResults.slice(0, 4).map((r) => ` • [vault/${r.type}] ${r.title} — v${r.version}`);
|
|
324
|
+
// Gap analysis
|
|
325
|
+
const gaps = [];
|
|
326
|
+
if (daysSinceUpdate !== null && daysSinceUpdate > 7) {
|
|
327
|
+
gaps.push(`Stale data — last update ${daysSinceUpdate} day${daysSinceUpdate !== 1 ? "s" : ""} ago`);
|
|
328
|
+
}
|
|
329
|
+
if (!vaultResults.some((r) => r.type === "research")) {
|
|
330
|
+
gaps.push("No formal research saved — only informal notes exist");
|
|
331
|
+
}
|
|
332
|
+
if (memResults.length < 3) {
|
|
333
|
+
gaps.push("Thin coverage — fewer than 3 semantic memories on this topic");
|
|
334
|
+
}
|
|
335
|
+
if (!vaultResults.some((r) => r.type === "execution")) {
|
|
336
|
+
gaps.push("No execution history — no trades or actions logged");
|
|
337
|
+
}
|
|
338
|
+
const lines = [
|
|
339
|
+
`🔮 **Intelligence Report: "${topic}"**`,
|
|
340
|
+
`Confidence: ${confidence} · ${memResults.length} semantic memories · ${vaultResults.length} vault entries`,
|
|
341
|
+
oldest ? `Coverage: ${new Date(oldest).toLocaleDateString("en-US")} – ${daysSinceUpdate === 0 ? "today" : daysSinceUpdate !== null ? `${daysSinceUpdate}d ago` : "unknown"}` : "",
|
|
342
|
+
``,
|
|
343
|
+
`**What you know:**`,
|
|
344
|
+
...memLines,
|
|
345
|
+
...(vaultLines.length ? ["", "**Vault entries:**", ...vaultLines] : []),
|
|
346
|
+
``,
|
|
347
|
+
];
|
|
348
|
+
if (gaps.length) {
|
|
349
|
+
lines.push(`**⚠️ Knowledge gaps:**`);
|
|
350
|
+
gaps.forEach(g => lines.push(` • ${g}`));
|
|
351
|
+
lines.push("");
|
|
352
|
+
}
|
|
353
|
+
lines.push(`**Suggested actions:**`);
|
|
354
|
+
if (gaps.some(g => g.includes("research") || g.includes("Stale"))) {
|
|
355
|
+
lines.push(`• \`swarm_research topic: "${topic}"\` — refresh with deep research`);
|
|
356
|
+
}
|
|
357
|
+
if (gaps.some(g => g.includes("Stale"))) {
|
|
358
|
+
lines.push(`• \`trigger_agent agentId: "market-monitor" params: { token: "${topic.split(" ")[0].toUpperCase()}" }\``);
|
|
359
|
+
}
|
|
360
|
+
lines.push(`• \`memory_context topic: "${topic}"\` — inject full context into your next prompt`);
|
|
361
|
+
lines.push(`• \`swarm_watch topic: "${topic}"\` — monitor this topic continuously`);
|
|
362
|
+
return { content: [{ type: "text", text: lines.filter(l => l !== undefined).join("\n") }] };
|
|
335
363
|
}
|
|
336
364
|
default:
|
|
337
365
|
return null;
|
package/dist/tools/miroshark.js
CHANGED
|
@@ -179,6 +179,9 @@ async function handleMirosharkTool(name, args) {
|
|
|
179
179
|
return { content: [{ type: "text", text: "simulation_id is required" }], isError: true };
|
|
180
180
|
}
|
|
181
181
|
const simId = a.simulation_id.trim();
|
|
182
|
+
if (!/^[a-zA-Z0-9_-]{5,100}$/.test(simId)) {
|
|
183
|
+
return { content: [{ type: "text", text: "Invalid simulation_id format." }], isError: true };
|
|
184
|
+
}
|
|
182
185
|
try {
|
|
183
186
|
// Check run status first
|
|
184
187
|
const runStatus = await miroJson(`/miroshark/api/simulation/${simId}/run-status`, "GET").catch(() => ({ runner_status: "idle" }));
|
|
@@ -273,15 +276,20 @@ async function handleMirosharkTool(name, args) {
|
|
|
273
276
|
// brief stays empty — non-critical
|
|
274
277
|
}
|
|
275
278
|
}
|
|
276
|
-
// Auto-save to vault if
|
|
277
|
-
|
|
279
|
+
// Auto-save to vault if brief was generated
|
|
280
|
+
let savedToVault = false;
|
|
278
281
|
if (brief) {
|
|
279
282
|
try {
|
|
280
283
|
await (0, convex_js_1.callConvex)("/vault/save", "POST", {
|
|
284
|
+
type: "research",
|
|
285
|
+
title: `MiroShark: ${a.scenario?.slice(0, 80) ?? simId}`,
|
|
286
|
+
content: brief,
|
|
281
287
|
key: `miroshark-${simId.slice(0, 8)}`,
|
|
282
|
-
|
|
288
|
+
agentId: "miroshark",
|
|
283
289
|
tags: ["miroshark", "simulation", "research"],
|
|
290
|
+
commitMsg: "miroshark auto-save",
|
|
284
291
|
}, "vault_save");
|
|
292
|
+
savedToVault = true;
|
|
285
293
|
}
|
|
286
294
|
catch {
|
|
287
295
|
// non-critical
|
|
@@ -299,7 +307,7 @@ async function handleMirosharkTool(name, args) {
|
|
|
299
307
|
lines.push(`**Agent Feed** (${Math.min(actions.length, 20)} of ${totalActions}):`);
|
|
300
308
|
lines.push(...feed);
|
|
301
309
|
}
|
|
302
|
-
if (
|
|
310
|
+
if (savedToVault) {
|
|
303
311
|
lines.push("", `_Findings auto-saved to vault as \`miroshark-${simId.slice(0, 8)}\`_`);
|
|
304
312
|
}
|
|
305
313
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
@@ -326,6 +334,9 @@ async function handleMirosharkTool(name, args) {
|
|
|
326
334
|
return { content: [{ type: "text", text: "simulation_id is required" }], isError: true };
|
|
327
335
|
}
|
|
328
336
|
const simId = a.simulation_id.trim();
|
|
337
|
+
if (!/^[a-zA-Z0-9_-]{5,100}$/.test(simId)) {
|
|
338
|
+
return { content: [{ type: "text", text: "Invalid simulation_id format." }], isError: true };
|
|
339
|
+
}
|
|
329
340
|
try {
|
|
330
341
|
await miroJson(`/miroshark/api/simulation/${simId}/stop`, "POST", {});
|
|
331
342
|
return { content: [{ type: "text", text: `⏹️ Simulation \`${simId}\` stopped.` }] };
|