@noelclaw/mcp 2.3.1 → 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 +159 -43
- 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 +89 -144
- 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
|
@@ -29,16 +29,17 @@ exports.MEMORY_TOOLS = [
|
|
|
29
29
|
name: "memory_add",
|
|
30
30
|
description: "Add content to your Noelclaw semantic memory — no setup needed, no extra API keys. " +
|
|
31
31
|
"Unlike vault_save, memory_add is instant: no versioning, no type required. " +
|
|
32
|
-
"Use for notes, decisions, preferences,
|
|
33
|
-
"
|
|
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?' " +
|
|
34
35
|
"will find it even without exact keywords.",
|
|
35
36
|
inputSchema: {
|
|
36
37
|
type: "object",
|
|
37
38
|
properties: {
|
|
38
|
-
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." },
|
|
39
40
|
title: { type: "string", description: "Optional title for this memory" },
|
|
40
41
|
tags: { type: "array", items: { type: "string" }, description: "Tags for grouping" },
|
|
41
|
-
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." },
|
|
42
43
|
},
|
|
43
44
|
required: ["content"],
|
|
44
45
|
},
|
|
@@ -47,7 +48,7 @@ exports.MEMORY_TOOLS = [
|
|
|
47
48
|
name: "memory_search",
|
|
48
49
|
description: "Semantic search across all your Noelclaw memories. Understands meaning, not just keywords — " +
|
|
49
50
|
"'low risk crypto yield' matches 'conservative DeFi strategies'. " +
|
|
50
|
-
"Also searches memories auto-synced from vault_save
|
|
51
|
+
"Also searches memories auto-synced from vault_save.",
|
|
51
52
|
inputSchema: {
|
|
52
53
|
type: "object",
|
|
53
54
|
properties: {
|
|
@@ -61,7 +62,7 @@ exports.MEMORY_TOOLS = [
|
|
|
61
62
|
name: "memory_context",
|
|
62
63
|
description: "Retrieve the most semantically relevant memories for a topic, formatted as AI-ready context. " +
|
|
63
64
|
"Use at the start of research tasks to prime with everything stored about a topic. " +
|
|
64
|
-
"
|
|
65
|
+
"Uses vector search — finds semantically related content, not just exact keyword matches.",
|
|
65
66
|
inputSchema: {
|
|
66
67
|
type: "object",
|
|
67
68
|
properties: {
|
|
@@ -82,19 +83,44 @@ exports.MEMORY_TOOLS = [
|
|
|
82
83
|
},
|
|
83
84
|
},
|
|
84
85
|
{
|
|
85
|
-
name: "
|
|
86
|
-
description: "
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"For full Google Drive / Gmail / Notion workspace sync, connect via the Noelclaw dashboard.",
|
|
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
90
|
inputSchema: {
|
|
91
91
|
type: "object",
|
|
92
92
|
properties: {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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.",
|
|
103
|
+
inputSchema: {
|
|
104
|
+
type: "object",
|
|
105
|
+
properties: {
|
|
106
|
+
id: { type: "string", description: "Memory ID to delete (from memory_search or memory_list results)" },
|
|
107
|
+
},
|
|
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)" },
|
|
96
122
|
},
|
|
97
|
-
required: ["
|
|
123
|
+
required: ["topic"],
|
|
98
124
|
},
|
|
99
125
|
},
|
|
100
126
|
];
|
|
@@ -113,10 +139,14 @@ const ContextSchema = zod_1.z.object({
|
|
|
113
139
|
topic: zod_1.z.string().min(1),
|
|
114
140
|
limit: zod_1.z.number().optional(),
|
|
115
141
|
});
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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(),
|
|
120
150
|
});
|
|
121
151
|
// ─── Handler ─────────────────────────────────────────────────────────────────
|
|
122
152
|
async function handleMemoryTool(name, args) {
|
|
@@ -174,7 +204,7 @@ async function handleMemoryTool(name, args) {
|
|
|
174
204
|
const { topic, limit = 8 } = parsed.data;
|
|
175
205
|
const results = await searchSupermemory(topic, limit);
|
|
176
206
|
if (!results.length)
|
|
177
|
-
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.` }] };
|
|
178
208
|
const contextParts = results.map((r, i) => {
|
|
179
209
|
const title = r.metadata?.title ? `### ${r.metadata.title}` : `### Memory ${i + 1}`;
|
|
180
210
|
return `${title}\n${r.content}`;
|
|
@@ -210,8 +240,8 @@ async function handleMemoryTool(name, args) {
|
|
|
210
240
|
`Status: ${status === "ok" ? "✅ Active" : status === "not_configured" ? "⏳ Setting up" : "⚠️ " + status}`,
|
|
211
241
|
``,
|
|
212
242
|
`**Auto-synced sources:**`,
|
|
213
|
-
`• vault_save
|
|
214
|
-
`• memory_add
|
|
243
|
+
`• vault_save — ✅`,
|
|
244
|
+
`• memory_add (URL indexing) — ✅`,
|
|
215
245
|
`• Google Drive / Gmail / Notion — connect at noelclaw.com`,
|
|
216
246
|
``,
|
|
217
247
|
`**Capabilities:** Semantic search · Vector context · 81.6% LongMemEval`,
|
|
@@ -219,31 +249,117 @@ async function handleMemoryTool(name, args) {
|
|
|
219
249
|
}],
|
|
220
250
|
};
|
|
221
251
|
}
|
|
222
|
-
case "
|
|
223
|
-
const parsed =
|
|
252
|
+
case "memory_list": {
|
|
253
|
+
const parsed = ListSchema.safeParse(args ?? {});
|
|
224
254
|
if (!parsed.success)
|
|
225
255
|
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
226
|
-
const {
|
|
227
|
-
const
|
|
228
|
-
const data = await (0, convex_js_1.callConvex)("/memory/add", "POST", {
|
|
229
|
-
content: inferredTitle,
|
|
230
|
-
sourceUrl: url,
|
|
231
|
-
metadata: { title: inferredTitle, tags, source: "memory_connect", connectedAt: Date.now() },
|
|
232
|
-
}).catch((err) => ({ error: err.message }));
|
|
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 }));
|
|
233
258
|
if (data?.error)
|
|
234
259
|
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
+
};
|
|
309
|
+
}
|
|
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}`;
|
|
322
|
+
});
|
|
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") }] };
|
|
247
363
|
}
|
|
248
364
|
default:
|
|
249
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.` }] };
|
package/dist/tools/os.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OS_TOOLS = void 0;
|
|
4
|
+
exports.handleOsTool = handleOsTool;
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const convex_js_1 = require("../convex.js");
|
|
7
|
+
const market_js_1 = require("./market.js");
|
|
8
|
+
const memory_js_1 = require("./memory.js");
|
|
9
|
+
exports.OS_TOOLS = [
|
|
10
|
+
{
|
|
11
|
+
name: "noel_status",
|
|
12
|
+
description: "Full system dashboard for the Noelclaw AI OS — memory usage, swarm health, active automations, " +
|
|
13
|
+
"recent vault research, and execution scores. Like `htop` but for your AI operating system. " +
|
|
14
|
+
"Run this to get a complete picture of what's running and what your OS currently knows.",
|
|
15
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: "noel_boot",
|
|
19
|
+
description: "Boot sequence for the Noelclaw AI OS — starts the swarm, loads live market prices, checks active automations, " +
|
|
20
|
+
"and returns a unified briefing. One command to wake up the entire operating system. " +
|
|
21
|
+
"Run this first to prime the system before any trading or research session.",
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: "object",
|
|
24
|
+
properties: {
|
|
25
|
+
focus: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "Optional: token or topic to focus today's session on (e.g. 'ETH', 'Base ecosystem')",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
required: [],
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "noel_shutdown",
|
|
35
|
+
description: "Clean shutdown of the Noelclaw AI OS — stops the swarm, saves a session summary to vault, and returns a final briefing. " +
|
|
36
|
+
"Run at the end of a trading or research session to persist all findings before signing off.",
|
|
37
|
+
inputSchema: {
|
|
38
|
+
type: "object",
|
|
39
|
+
properties: {
|
|
40
|
+
note: {
|
|
41
|
+
type: "string",
|
|
42
|
+
description: "Optional: a note to save with the session summary (e.g. 'Closed ETH position, watching BTC')",
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
required: [],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
const BootSchema = zod_1.z.object({ focus: zod_1.z.string().optional() });
|
|
50
|
+
const ShutdownSchema = zod_1.z.object({ note: zod_1.z.string().max(500).optional() });
|
|
51
|
+
async function handleOsTool(name, args) {
|
|
52
|
+
switch (name) {
|
|
53
|
+
case "noel_status": {
|
|
54
|
+
const [memRes, swarmRes, autoRes, vaultRes, scoresRes] = await Promise.allSettled([
|
|
55
|
+
(0, convex_js_1.callConvex)("/memory/profile", "GET"),
|
|
56
|
+
(0, convex_js_1.callConvex)("/swarm/status", "GET", undefined, "get_swarm_status"),
|
|
57
|
+
(0, convex_js_1.callConvex)("/automations/list", "GET", undefined, "list_automations"),
|
|
58
|
+
(0, convex_js_1.callConvex)("/vault/list?type=research&limit=5", "GET", undefined, "noel_status"),
|
|
59
|
+
(0, convex_js_1.callConvex)("/swarm/scores", "GET", undefined, "get_execution_scores"),
|
|
60
|
+
]);
|
|
61
|
+
const mem = memRes.status === "fulfilled" ? memRes.value : null;
|
|
62
|
+
const swarm = swarmRes.status === "fulfilled" ? swarmRes.value : null;
|
|
63
|
+
const autos = autoRes.status === "fulfilled" ? autoRes.value : null;
|
|
64
|
+
const vault = vaultRes.status === "fulfilled" ? vaultRes.value : null;
|
|
65
|
+
const scores = scoresRes.status === "fulfilled" ? scoresRes.value : null;
|
|
66
|
+
const automations = autos?.automations ?? [];
|
|
67
|
+
const activeAutos = automations.filter((a) => a.status === "active");
|
|
68
|
+
const vaultEntries = vault?.entries ?? [];
|
|
69
|
+
const allScores = (scores?.scores ?? []).sort((a, b) => b.lastScore - a.lastScore);
|
|
70
|
+
const topScore = allScores[0];
|
|
71
|
+
const swarmActive = swarm?.active ?? false;
|
|
72
|
+
const memTotal = mem?.total ?? 0;
|
|
73
|
+
const memStatus = mem?.status ?? "unknown";
|
|
74
|
+
const lines = [
|
|
75
|
+
`**Noelclaw AI OS — System Status**`,
|
|
76
|
+
`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`,
|
|
77
|
+
``,
|
|
78
|
+
`🧠 **Memory** ${memStatus === "ok" ? "✅" : "⚠️"} ${memTotal} memories · Space: ${mem?.space ?? "—"}`,
|
|
79
|
+
`🤖 **Swarm** ${swarmActive ? "✅ Active" : "⏹️ Offline"} · ${swarm?.memory?.length ?? 0} shared memory entries`,
|
|
80
|
+
`⚡ **Automations** ${activeAutos.length} active of ${automations.length} total`,
|
|
81
|
+
allScores.length
|
|
82
|
+
? `📊 **Top Skill** ${topScore.skillName} — ${((topScore.lastScore ?? 0) * 100).toFixed(0)}% · ${topScore.successCount}W/${topScore.failCount}L`
|
|
83
|
+
: `📊 **Skills** No execution history yet`,
|
|
84
|
+
``,
|
|
85
|
+
];
|
|
86
|
+
if (activeAutos.length > 0) {
|
|
87
|
+
lines.push(`**Active Automations:**`);
|
|
88
|
+
for (const a of activeAutos.slice(0, 5)) {
|
|
89
|
+
const next = a.nextRunAt ? ` · next ${new Date(a.nextRunAt).toUTCString()}` : "";
|
|
90
|
+
lines.push(` • ${a.name} — ${a.triggerType}${next}`);
|
|
91
|
+
}
|
|
92
|
+
lines.push("");
|
|
93
|
+
}
|
|
94
|
+
if (vaultEntries.length > 0) {
|
|
95
|
+
lines.push(`**Recent Research:**`);
|
|
96
|
+
for (const e of vaultEntries) {
|
|
97
|
+
lines.push(` • [${e.agentId ?? "vault"}] ${e.title}`);
|
|
98
|
+
}
|
|
99
|
+
lines.push("");
|
|
100
|
+
}
|
|
101
|
+
lines.push(swarmActive
|
|
102
|
+
? `💡 Run \`swarm_pulse\` for a live snapshot from all agents.`
|
|
103
|
+
: `💡 Run \`noel_boot\` to wake up the full system.`);
|
|
104
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
105
|
+
}
|
|
106
|
+
case "noel_boot": {
|
|
107
|
+
const parsed = BootSchema.safeParse(args ?? {});
|
|
108
|
+
if (!parsed.success)
|
|
109
|
+
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
110
|
+
const { focus } = parsed.data;
|
|
111
|
+
const [swarmRes, marketRes, autoRes, memRes, focusRes] = await Promise.allSettled([
|
|
112
|
+
(0, convex_js_1.callConvex)("/swarm/start", "POST", {}, "start_swarm"),
|
|
113
|
+
(0, market_js_1.fetchMarketSnapshot)(),
|
|
114
|
+
(0, convex_js_1.callConvex)("/automations/list", "GET", undefined, "list_automations"),
|
|
115
|
+
(0, convex_js_1.callConvex)("/memory/profile", "GET"),
|
|
116
|
+
focus ? (0, memory_js_1.searchSupermemory)(focus, 4) : Promise.resolve([]),
|
|
117
|
+
]);
|
|
118
|
+
const swarm = swarmRes.status === "fulfilled" ? swarmRes.value : null;
|
|
119
|
+
const market = marketRes.status === "fulfilled" ? marketRes.value : null;
|
|
120
|
+
const autos = autoRes.status === "fulfilled" ? autoRes.value : null;
|
|
121
|
+
const mem = memRes.status === "fulfilled" ? memRes.value : null;
|
|
122
|
+
const focusMem = focusRes.status === "fulfilled" ? focusRes.value : [];
|
|
123
|
+
const automations = autos?.automations ?? [];
|
|
124
|
+
const activeAutos = automations.filter((a) => a.status === "active");
|
|
125
|
+
const memTotal = mem?.total ?? 0;
|
|
126
|
+
const p = (n) => `$${n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
127
|
+
const lines = [
|
|
128
|
+
`🚀 **Noelclaw AI OS — Boot Complete**`,
|
|
129
|
+
`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`,
|
|
130
|
+
`${new Date().toUTCString()}`,
|
|
131
|
+
``,
|
|
132
|
+
`**Subsystems:**`,
|
|
133
|
+
` 🤖 Swarm ${swarm?.success ? `✅ Online · session ${swarm.sessionId ?? "active"}` : "⚠️ Could not start"}`,
|
|
134
|
+
` 🧠 Memory ✅ ${memTotal} memories ready`,
|
|
135
|
+
` ⚡ Automations ${activeAutos.length} active of ${automations.length}`,
|
|
136
|
+
``,
|
|
137
|
+
];
|
|
138
|
+
if (market) {
|
|
139
|
+
lines.push(`**Market Snapshot:**`);
|
|
140
|
+
lines.push(` BTC ${p(market.btc)} · ETH ${p(market.eth)} · SOL ${p(market.sol)}`);
|
|
141
|
+
lines.push("");
|
|
142
|
+
}
|
|
143
|
+
if (activeAutos.length > 0) {
|
|
144
|
+
lines.push(`**Active Automations:**`);
|
|
145
|
+
for (const a of activeAutos.slice(0, 5)) {
|
|
146
|
+
const next = a.nextRunAt ? ` · runs ${new Date(a.nextRunAt).toUTCString()}` : "";
|
|
147
|
+
lines.push(` • ${a.name}${next}`);
|
|
148
|
+
}
|
|
149
|
+
lines.push("");
|
|
150
|
+
}
|
|
151
|
+
if (focus && focusMem.length > 0) {
|
|
152
|
+
lines.push(`**Memory context for "${focus}" (${focusMem.length} items):**`);
|
|
153
|
+
for (const r of focusMem) {
|
|
154
|
+
const title = r.metadata?.title ?? r.content.slice(0, 70).replace(/\n/g, " ");
|
|
155
|
+
lines.push(` • ${title}`);
|
|
156
|
+
}
|
|
157
|
+
lines.push("");
|
|
158
|
+
}
|
|
159
|
+
lines.push(`**Quick actions:**`);
|
|
160
|
+
lines.push(` • \`swarm_pulse\` — live readings from all agents`);
|
|
161
|
+
if (focus) {
|
|
162
|
+
lines.push(` • \`swarm_research topic: "${focus}"\` — deep research session`);
|
|
163
|
+
lines.push(` • \`memory_insight topic: "${focus}"\` — full intelligence report`);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
lines.push(` • \`swarm_research topic: "BTC"\` — start morning research`);
|
|
167
|
+
lines.push(` • \`noel_status\` — full system dashboard`);
|
|
168
|
+
}
|
|
169
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
170
|
+
}
|
|
171
|
+
case "noel_shutdown": {
|
|
172
|
+
const parsed = ShutdownSchema.safeParse(args ?? {});
|
|
173
|
+
if (!parsed.success)
|
|
174
|
+
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
175
|
+
const { note } = parsed.data;
|
|
176
|
+
const [swarmRes, vaultRes] = await Promise.allSettled([
|
|
177
|
+
(0, convex_js_1.callConvex)("/swarm/stop", "POST", {}, "stop_swarm"),
|
|
178
|
+
(0, convex_js_1.callConvex)("/vault/list?type=research&limit=5", "GET", undefined, "noel_shutdown"),
|
|
179
|
+
]);
|
|
180
|
+
const swarm = swarmRes.status === "fulfilled" ? swarmRes.value : null;
|
|
181
|
+
const vault = vaultRes.status === "fulfilled" ? vaultRes.value : null;
|
|
182
|
+
const vaultEntries = vault?.entries ?? [];
|
|
183
|
+
const now = new Date();
|
|
184
|
+
const sessionKey = `session/shutdown-${now.toISOString().slice(0, 10)}-${now.getHours()}h`;
|
|
185
|
+
const summaryContent = [
|
|
186
|
+
`# Session Shutdown — ${now.toUTCString()}`,
|
|
187
|
+
note ? `\nNote: ${note}` : "",
|
|
188
|
+
`\n## Recent Research`,
|
|
189
|
+
...vaultEntries.map((e) => `• [${e.agentId ?? "vault"}] ${e.title}`),
|
|
190
|
+
].filter(Boolean).join("\n");
|
|
191
|
+
await (0, convex_js_1.callConvex)("/vault/save", "POST", {
|
|
192
|
+
type: "memory",
|
|
193
|
+
title: `Session: ${now.toLocaleDateString("en-US")}${note ? ` — ${note.slice(0, 50)}` : ""}`,
|
|
194
|
+
content: summaryContent,
|
|
195
|
+
key: sessionKey,
|
|
196
|
+
agentId: "os",
|
|
197
|
+
tags: ["session", "shutdown"],
|
|
198
|
+
commitMsg: "noel_shutdown session save",
|
|
199
|
+
}, "noel_shutdown").catch(() => { });
|
|
200
|
+
const lines = [
|
|
201
|
+
`⏹️ **Noelclaw AI OS — Shutdown**`,
|
|
202
|
+
`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`,
|
|
203
|
+
`${now.toUTCString()}`,
|
|
204
|
+
``,
|
|
205
|
+
`🤖 Swarm ${swarm?.success ? "✅ Stopped" : "⚠️ Already offline"}`,
|
|
206
|
+
`💾 Session ✅ Saved to vault: \`${sessionKey}\``,
|
|
207
|
+
note ? `📝 Note ${note}` : "",
|
|
208
|
+
``,
|
|
209
|
+
];
|
|
210
|
+
if (vaultEntries.length > 0) {
|
|
211
|
+
lines.push(`**Session research (${vaultEntries.length} entries):**`);
|
|
212
|
+
for (const e of vaultEntries) {
|
|
213
|
+
lines.push(` • [${e.agentId ?? "vault"}] ${e.title}`);
|
|
214
|
+
}
|
|
215
|
+
lines.push("");
|
|
216
|
+
}
|
|
217
|
+
lines.push(`Run \`noel_boot\` to start a new session.`);
|
|
218
|
+
return { content: [{ type: "text", text: lines.filter(l => l !== undefined && l !== "").join("\n") }] };
|
|
219
|
+
}
|
|
220
|
+
default:
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
}
|