claude-flow 3.5.24 → 3.5.26
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/package.json +2 -1
- package/v3/@claude-flow/cli/dist/src/commands/index.js +2 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-tools.js +2 -2
- package/v3/@claude-flow/cli/dist/src/mcp-tools/config-tools.js +10 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/index.d.ts +2 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/index.js +2 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/memory-tools.js +2 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/ruvllm-tools.d.ts +9 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/ruvllm-tools.js +283 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/swarm-tools.d.ts +2 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/swarm-tools.js +216 -30
- package/v3/@claude-flow/cli/dist/src/mcp-tools/wasm-agent-tools.d.ts +9 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/wasm-agent-tools.js +230 -0
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +65 -6
- package/v3/@claude-flow/cli/dist/src/ruvector/agent-wasm.d.ts +182 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/agent-wasm.js +316 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/index.d.ts +2 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/index.js +4 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/ruvllm-wasm.d.ts +179 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/ruvllm-wasm.js +363 -0
- package/v3/@claude-flow/cli/dist/src/transfer/storage/gcs.js +22 -6
- package/v3/@claude-flow/cli/package.json +6 -3
|
@@ -1,82 +1,212 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Swarm MCP Tools for CLI
|
|
3
3
|
*
|
|
4
|
-
* Tool definitions for swarm coordination.
|
|
4
|
+
* Tool definitions for swarm coordination with file-based state persistence.
|
|
5
|
+
* Replaces previous stub implementations with real state tracking.
|
|
5
6
|
*/
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
// Swarm state persistence
|
|
10
|
+
const SWARM_DIR = '.claude-flow/swarm';
|
|
11
|
+
const SWARM_STATE_FILE = 'swarm-state.json';
|
|
12
|
+
function getSwarmDir() {
|
|
13
|
+
return join(process.cwd(), SWARM_DIR);
|
|
14
|
+
}
|
|
15
|
+
function getSwarmStatePath() {
|
|
16
|
+
return join(getSwarmDir(), SWARM_STATE_FILE);
|
|
17
|
+
}
|
|
18
|
+
function ensureSwarmDir() {
|
|
19
|
+
const dir = getSwarmDir();
|
|
20
|
+
if (!existsSync(dir)) {
|
|
21
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function loadSwarmStore() {
|
|
25
|
+
try {
|
|
26
|
+
const path = getSwarmStatePath();
|
|
27
|
+
if (existsSync(path)) {
|
|
28
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch { /* return default */ }
|
|
32
|
+
return { swarms: {}, version: '3.0.0' };
|
|
33
|
+
}
|
|
34
|
+
function saveSwarmStore(store) {
|
|
35
|
+
ensureSwarmDir();
|
|
36
|
+
writeFileSync(getSwarmStatePath(), JSON.stringify(store, null, 2), 'utf-8');
|
|
37
|
+
}
|
|
38
|
+
// Input validation
|
|
39
|
+
const VALID_TOPOLOGIES = new Set([
|
|
40
|
+
'hierarchical', 'mesh', 'hierarchical-mesh', 'ring', 'star', 'hybrid', 'adaptive',
|
|
41
|
+
]);
|
|
6
42
|
export const swarmTools = [
|
|
7
43
|
{
|
|
8
44
|
name: 'swarm_init',
|
|
9
|
-
description: 'Initialize a swarm',
|
|
45
|
+
description: 'Initialize a swarm with persistent state tracking',
|
|
10
46
|
category: 'swarm',
|
|
11
47
|
inputSchema: {
|
|
12
48
|
type: 'object',
|
|
13
49
|
properties: {
|
|
14
|
-
topology: { type: 'string', description: 'Swarm topology type' },
|
|
15
|
-
maxAgents: { type: 'number', description: 'Maximum number of agents' },
|
|
16
|
-
|
|
50
|
+
topology: { type: 'string', description: 'Swarm topology type (hierarchical, mesh, hierarchical-mesh, ring, star, hybrid, adaptive)' },
|
|
51
|
+
maxAgents: { type: 'number', description: 'Maximum number of agents (1-50)' },
|
|
52
|
+
strategy: { type: 'string', description: 'Agent strategy (specialized, balanced, adaptive)' },
|
|
53
|
+
config: { type: 'object', description: 'Additional swarm configuration' },
|
|
17
54
|
},
|
|
18
55
|
},
|
|
19
56
|
handler: async (input) => {
|
|
20
57
|
const topology = input.topology || 'hierarchical-mesh';
|
|
21
|
-
const maxAgents = input.maxAgents || 15;
|
|
58
|
+
const maxAgents = Math.min(Math.max(input.maxAgents || 15, 1), 50);
|
|
59
|
+
const strategy = input.strategy || 'specialized';
|
|
22
60
|
const config = (input.config || {});
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
61
|
+
if (!VALID_TOPOLOGIES.has(topology)) {
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
error: `Invalid topology: ${topology}. Valid: ${[...VALID_TOPOLOGIES].join(', ')}`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const swarmId = `swarm-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
68
|
+
const now = new Date().toISOString();
|
|
69
|
+
const swarmState = {
|
|
70
|
+
swarmId,
|
|
26
71
|
topology,
|
|
27
|
-
|
|
72
|
+
maxAgents,
|
|
73
|
+
status: 'running',
|
|
74
|
+
agents: [],
|
|
75
|
+
tasks: [],
|
|
28
76
|
config: {
|
|
29
77
|
topology,
|
|
30
78
|
maxAgents,
|
|
31
|
-
|
|
79
|
+
strategy,
|
|
32
80
|
communicationProtocol: config.communicationProtocol || 'message-bus',
|
|
33
81
|
autoScaling: config.autoScaling ?? true,
|
|
34
82
|
consensusMechanism: config.consensusMechanism || 'majority',
|
|
35
83
|
},
|
|
84
|
+
createdAt: now,
|
|
85
|
+
updatedAt: now,
|
|
86
|
+
};
|
|
87
|
+
const store = loadSwarmStore();
|
|
88
|
+
store.swarms[swarmId] = swarmState;
|
|
89
|
+
saveSwarmStore(store);
|
|
90
|
+
return {
|
|
91
|
+
success: true,
|
|
92
|
+
swarmId,
|
|
93
|
+
topology,
|
|
94
|
+
strategy,
|
|
95
|
+
maxAgents,
|
|
96
|
+
initializedAt: now,
|
|
97
|
+
config: swarmState.config,
|
|
98
|
+
persisted: true,
|
|
36
99
|
};
|
|
37
100
|
},
|
|
38
101
|
},
|
|
39
102
|
{
|
|
40
103
|
name: 'swarm_status',
|
|
41
|
-
description: 'Get swarm status',
|
|
104
|
+
description: 'Get swarm status from persistent state',
|
|
42
105
|
category: 'swarm',
|
|
43
106
|
inputSchema: {
|
|
44
107
|
type: 'object',
|
|
45
108
|
properties: {
|
|
46
|
-
swarmId: { type: 'string', description: 'Swarm ID' },
|
|
109
|
+
swarmId: { type: 'string', description: 'Swarm ID (omit for most recent)' },
|
|
47
110
|
},
|
|
48
111
|
},
|
|
49
112
|
handler: async (input) => {
|
|
113
|
+
const store = loadSwarmStore();
|
|
114
|
+
const swarmId = input.swarmId;
|
|
115
|
+
if (swarmId && store.swarms[swarmId]) {
|
|
116
|
+
const swarm = store.swarms[swarmId];
|
|
117
|
+
return {
|
|
118
|
+
swarmId: swarm.swarmId,
|
|
119
|
+
status: swarm.status,
|
|
120
|
+
topology: swarm.topology,
|
|
121
|
+
maxAgents: swarm.maxAgents,
|
|
122
|
+
agentCount: swarm.agents.length,
|
|
123
|
+
taskCount: swarm.tasks.length,
|
|
124
|
+
config: swarm.config,
|
|
125
|
+
createdAt: swarm.createdAt,
|
|
126
|
+
updatedAt: swarm.updatedAt,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
// Return most recent swarm if no ID specified
|
|
130
|
+
const swarmIds = Object.keys(store.swarms);
|
|
131
|
+
if (swarmIds.length === 0) {
|
|
132
|
+
return {
|
|
133
|
+
status: 'no_swarm',
|
|
134
|
+
message: 'No active swarms. Use swarm_init to create one.',
|
|
135
|
+
totalSwarms: 0,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const latest = swarmIds
|
|
139
|
+
.map(id => store.swarms[id])
|
|
140
|
+
.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())[0];
|
|
50
141
|
return {
|
|
51
|
-
swarmId:
|
|
52
|
-
status:
|
|
53
|
-
|
|
54
|
-
|
|
142
|
+
swarmId: latest.swarmId,
|
|
143
|
+
status: latest.status,
|
|
144
|
+
topology: latest.topology,
|
|
145
|
+
maxAgents: latest.maxAgents,
|
|
146
|
+
agentCount: latest.agents.length,
|
|
147
|
+
taskCount: latest.tasks.length,
|
|
148
|
+
config: latest.config,
|
|
149
|
+
createdAt: latest.createdAt,
|
|
150
|
+
updatedAt: latest.updatedAt,
|
|
151
|
+
totalSwarms: swarmIds.length,
|
|
55
152
|
};
|
|
56
153
|
},
|
|
57
154
|
},
|
|
58
155
|
{
|
|
59
156
|
name: 'swarm_shutdown',
|
|
60
|
-
description: 'Shutdown a swarm',
|
|
157
|
+
description: 'Shutdown a swarm and update persistent state',
|
|
61
158
|
category: 'swarm',
|
|
62
159
|
inputSchema: {
|
|
63
160
|
type: 'object',
|
|
64
161
|
properties: {
|
|
65
|
-
swarmId: { type: 'string', description: 'Swarm ID' },
|
|
66
|
-
graceful: { type: 'boolean', description: 'Graceful shutdown' },
|
|
162
|
+
swarmId: { type: 'string', description: 'Swarm ID to shutdown' },
|
|
163
|
+
graceful: { type: 'boolean', description: 'Graceful shutdown (default: true)' },
|
|
67
164
|
},
|
|
68
165
|
},
|
|
69
166
|
handler: async (input) => {
|
|
167
|
+
const store = loadSwarmStore();
|
|
168
|
+
const swarmId = input.swarmId;
|
|
169
|
+
// Find the swarm
|
|
170
|
+
let target;
|
|
171
|
+
if (swarmId && store.swarms[swarmId]) {
|
|
172
|
+
target = store.swarms[swarmId];
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
// Shutdown most recent running swarm
|
|
176
|
+
const running = Object.values(store.swarms)
|
|
177
|
+
.filter(s => s.status === 'running')
|
|
178
|
+
.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
|
|
179
|
+
target = running[0];
|
|
180
|
+
}
|
|
181
|
+
if (!target) {
|
|
182
|
+
return {
|
|
183
|
+
success: false,
|
|
184
|
+
error: swarmId ? `Swarm ${swarmId} not found` : 'No running swarms to shutdown',
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (target.status === 'terminated') {
|
|
188
|
+
return {
|
|
189
|
+
success: false,
|
|
190
|
+
swarmId: target.swarmId,
|
|
191
|
+
error: 'Swarm already terminated',
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
target.status = 'terminated';
|
|
195
|
+
target.updatedAt = new Date().toISOString();
|
|
196
|
+
saveSwarmStore(store);
|
|
70
197
|
return {
|
|
71
198
|
success: true,
|
|
72
|
-
swarmId:
|
|
199
|
+
swarmId: target.swarmId,
|
|
73
200
|
terminated: true,
|
|
201
|
+
graceful: input.graceful ?? true,
|
|
202
|
+
agentsTerminated: target.agents.length,
|
|
203
|
+
terminatedAt: target.updatedAt,
|
|
74
204
|
};
|
|
75
205
|
},
|
|
76
206
|
},
|
|
77
207
|
{
|
|
78
208
|
name: 'swarm_health',
|
|
79
|
-
description: 'Check swarm health status',
|
|
209
|
+
description: 'Check swarm health status with real state inspection',
|
|
80
210
|
category: 'swarm',
|
|
81
211
|
inputSchema: {
|
|
82
212
|
type: 'object',
|
|
@@ -85,15 +215,71 @@ export const swarmTools = [
|
|
|
85
215
|
},
|
|
86
216
|
},
|
|
87
217
|
handler: async (input) => {
|
|
218
|
+
const store = loadSwarmStore();
|
|
219
|
+
const swarmId = input.swarmId;
|
|
220
|
+
// Find the swarm
|
|
221
|
+
let target;
|
|
222
|
+
if (swarmId) {
|
|
223
|
+
target = store.swarms[swarmId];
|
|
224
|
+
if (!target) {
|
|
225
|
+
return {
|
|
226
|
+
status: 'not_found',
|
|
227
|
+
healthy: false,
|
|
228
|
+
checks: [
|
|
229
|
+
{ name: 'swarm_exists', status: 'fail', message: `Swarm ${swarmId} not found` },
|
|
230
|
+
],
|
|
231
|
+
checkedAt: new Date().toISOString(),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
const running = Object.values(store.swarms)
|
|
237
|
+
.filter(s => s.status === 'running')
|
|
238
|
+
.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
|
|
239
|
+
target = running[0];
|
|
240
|
+
}
|
|
241
|
+
if (!target) {
|
|
242
|
+
return {
|
|
243
|
+
status: 'no_swarm',
|
|
244
|
+
healthy: false,
|
|
245
|
+
checks: [
|
|
246
|
+
{ name: 'swarm_exists', status: 'fail', message: 'No active swarm found' },
|
|
247
|
+
],
|
|
248
|
+
checkedAt: new Date().toISOString(),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
const isRunning = target.status === 'running';
|
|
252
|
+
const stateFileExists = existsSync(getSwarmStatePath());
|
|
253
|
+
const checks = [
|
|
254
|
+
{
|
|
255
|
+
name: 'coordinator',
|
|
256
|
+
status: isRunning ? 'ok' : 'warn',
|
|
257
|
+
message: isRunning ? 'Coordinator active' : `Swarm status: ${target.status}`,
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
name: 'agents',
|
|
261
|
+
status: target.agents.length > 0 ? 'ok' : 'info',
|
|
262
|
+
message: `${target.agents.length} agents registered (max: ${target.maxAgents})`,
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
name: 'persistence',
|
|
266
|
+
status: stateFileExists ? 'ok' : 'warn',
|
|
267
|
+
message: stateFileExists ? 'State file persisted' : 'State file missing',
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: 'topology',
|
|
271
|
+
status: 'ok',
|
|
272
|
+
message: `Topology: ${target.topology}`,
|
|
273
|
+
},
|
|
274
|
+
];
|
|
275
|
+
const healthy = isRunning && stateFileExists;
|
|
88
276
|
return {
|
|
89
|
-
status: 'healthy',
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
{ name: 'messaging', status: 'ok', message: 'Message bus active' },
|
|
96
|
-
],
|
|
277
|
+
status: healthy ? 'healthy' : 'degraded',
|
|
278
|
+
healthy,
|
|
279
|
+
swarmId: target.swarmId,
|
|
280
|
+
topology: target.topology,
|
|
281
|
+
agentCount: target.agents.length,
|
|
282
|
+
checks,
|
|
97
283
|
checkedAt: new Date().toISOString(),
|
|
98
284
|
};
|
|
99
285
|
},
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WASM Agent MCP Tools
|
|
3
|
+
*
|
|
4
|
+
* Exposes @ruvector/rvagent-wasm operations via MCP protocol.
|
|
5
|
+
* All tools gracefully degrade when the WASM package is not installed.
|
|
6
|
+
*/
|
|
7
|
+
import type { MCPTool } from './types.js';
|
|
8
|
+
export declare const wasmAgentTools: MCPTool[];
|
|
9
|
+
//# sourceMappingURL=wasm-agent-tools.d.ts.map
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WASM Agent MCP Tools
|
|
3
|
+
*
|
|
4
|
+
* Exposes @ruvector/rvagent-wasm operations via MCP protocol.
|
|
5
|
+
* All tools gracefully degrade when the WASM package is not installed.
|
|
6
|
+
*/
|
|
7
|
+
async function loadAgentWasm() {
|
|
8
|
+
const mod = await import('../ruvector/agent-wasm.js');
|
|
9
|
+
return mod;
|
|
10
|
+
}
|
|
11
|
+
export const wasmAgentTools = [
|
|
12
|
+
{
|
|
13
|
+
name: 'wasm_agent_create',
|
|
14
|
+
description: 'Create a sandboxed WASM agent with virtual filesystem (no OS access). Optionally use a gallery template.',
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
template: { type: 'string', description: 'Gallery template name (coder, researcher, tester, reviewer, security, swarm)' },
|
|
19
|
+
model: { type: 'string', description: 'Model identifier (default: anthropic:claude-sonnet-4-20250514)' },
|
|
20
|
+
instructions: { type: 'string', description: 'System instructions for the agent' },
|
|
21
|
+
maxTurns: { type: 'number', description: 'Max conversation turns (default: 50)' },
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
handler: async (args) => {
|
|
25
|
+
try {
|
|
26
|
+
const wasm = await loadAgentWasm();
|
|
27
|
+
if (args.template) {
|
|
28
|
+
const info = await wasm.createAgentFromTemplate(args.template);
|
|
29
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: true, agent: info, source: 'gallery' }, null, 2) }] };
|
|
30
|
+
}
|
|
31
|
+
const info = await wasm.createWasmAgent({
|
|
32
|
+
model: args.model,
|
|
33
|
+
instructions: args.instructions,
|
|
34
|
+
maxTurns: args.maxTurns,
|
|
35
|
+
});
|
|
36
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: true, agent: info }, null, 2) }] };
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'wasm_agent_prompt',
|
|
45
|
+
description: 'Send a prompt to a WASM agent and get a response.',
|
|
46
|
+
inputSchema: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
agentId: { type: 'string', description: 'WASM agent ID' },
|
|
50
|
+
input: { type: 'string', description: 'User prompt to send' },
|
|
51
|
+
},
|
|
52
|
+
required: ['agentId', 'input'],
|
|
53
|
+
},
|
|
54
|
+
handler: async (args) => {
|
|
55
|
+
try {
|
|
56
|
+
const wasm = await loadAgentWasm();
|
|
57
|
+
const result = await wasm.promptWasmAgent(args.agentId, args.input);
|
|
58
|
+
return { content: [{ type: 'text', text: result }] };
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'wasm_agent_tool',
|
|
67
|
+
description: 'Execute a tool on a WASM agent sandbox. Tools: read_file, write_file, edit_file, write_todos, list_files. Use flat format: {tool, path, content, ...}.',
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: {
|
|
71
|
+
agentId: { type: 'string', description: 'WASM agent ID' },
|
|
72
|
+
toolName: { type: 'string', description: 'Tool name (read_file, write_file, edit_file, write_todos, list_files)' },
|
|
73
|
+
toolInput: { type: 'object', description: 'Tool parameters (flat: {path, content, old_string, new_string, todos})' },
|
|
74
|
+
},
|
|
75
|
+
required: ['agentId', 'toolName'],
|
|
76
|
+
},
|
|
77
|
+
handler: async (args) => {
|
|
78
|
+
try {
|
|
79
|
+
const wasm = await loadAgentWasm();
|
|
80
|
+
// Flat format: {tool: 'write_file', path: '...', content: '...'}
|
|
81
|
+
const toolCall = {
|
|
82
|
+
tool: args.toolName,
|
|
83
|
+
...(args.toolInput ?? {}),
|
|
84
|
+
};
|
|
85
|
+
const result = await wasm.executeWasmTool(args.agentId, toolCall);
|
|
86
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'wasm_agent_list',
|
|
95
|
+
description: 'List all active WASM agents.',
|
|
96
|
+
inputSchema: { type: 'object', properties: {} },
|
|
97
|
+
handler: async () => {
|
|
98
|
+
try {
|
|
99
|
+
const wasm = await loadAgentWasm();
|
|
100
|
+
const agents = wasm.listWasmAgents();
|
|
101
|
+
return { content: [{ type: 'text', text: JSON.stringify({ agents, count: agents.length }, null, 2) }] };
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'wasm_agent_terminate',
|
|
110
|
+
description: 'Terminate a WASM agent and free resources.',
|
|
111
|
+
inputSchema: {
|
|
112
|
+
type: 'object',
|
|
113
|
+
properties: {
|
|
114
|
+
agentId: { type: 'string', description: 'WASM agent ID' },
|
|
115
|
+
},
|
|
116
|
+
required: ['agentId'],
|
|
117
|
+
},
|
|
118
|
+
handler: async (args) => {
|
|
119
|
+
try {
|
|
120
|
+
const wasm = await loadAgentWasm();
|
|
121
|
+
const ok = wasm.terminateWasmAgent(args.agentId);
|
|
122
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: ok }) }] };
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'wasm_agent_files',
|
|
131
|
+
description: 'Get a WASM agent\'s available tools and info.',
|
|
132
|
+
inputSchema: {
|
|
133
|
+
type: 'object',
|
|
134
|
+
properties: {
|
|
135
|
+
agentId: { type: 'string', description: 'WASM agent ID' },
|
|
136
|
+
},
|
|
137
|
+
required: ['agentId'],
|
|
138
|
+
},
|
|
139
|
+
handler: async (args) => {
|
|
140
|
+
try {
|
|
141
|
+
const wasm = await loadAgentWasm();
|
|
142
|
+
const tools = wasm.getWasmAgentTools(args.agentId);
|
|
143
|
+
const info = wasm.getWasmAgent(args.agentId);
|
|
144
|
+
return { content: [{ type: 'text', text: JSON.stringify({ tools, fileCount: info?.fileCount ?? 0, turnCount: info?.turnCount ?? 0 }, null, 2) }] };
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'wasm_agent_export',
|
|
153
|
+
description: 'Export a WASM agent\'s full state (config, filesystem, conversation) as JSON.',
|
|
154
|
+
inputSchema: {
|
|
155
|
+
type: 'object',
|
|
156
|
+
properties: {
|
|
157
|
+
agentId: { type: 'string', description: 'WASM agent ID' },
|
|
158
|
+
},
|
|
159
|
+
required: ['agentId'],
|
|
160
|
+
},
|
|
161
|
+
handler: async (args) => {
|
|
162
|
+
try {
|
|
163
|
+
const wasm = await loadAgentWasm();
|
|
164
|
+
const state = wasm.exportWasmState(args.agentId);
|
|
165
|
+
return { content: [{ type: 'text', text: state }] };
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: 'wasm_gallery_list',
|
|
174
|
+
description: 'List all available WASM agent gallery templates (Coder, Researcher, Tester, Reviewer, Security, Swarm).',
|
|
175
|
+
inputSchema: { type: 'object', properties: {} },
|
|
176
|
+
handler: async () => {
|
|
177
|
+
try {
|
|
178
|
+
const wasm = await loadAgentWasm();
|
|
179
|
+
const templates = await wasm.listGalleryTemplates();
|
|
180
|
+
return { content: [{ type: 'text', text: JSON.stringify({ templates, count: templates.length }, null, 2) }] };
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: 'wasm_gallery_search',
|
|
189
|
+
description: 'Search WASM agent gallery templates by query.',
|
|
190
|
+
inputSchema: {
|
|
191
|
+
type: 'object',
|
|
192
|
+
properties: {
|
|
193
|
+
query: { type: 'string', description: 'Search query' },
|
|
194
|
+
},
|
|
195
|
+
required: ['query'],
|
|
196
|
+
},
|
|
197
|
+
handler: async (args) => {
|
|
198
|
+
try {
|
|
199
|
+
const wasm = await loadAgentWasm();
|
|
200
|
+
const results = await wasm.searchGalleryTemplates(args.query);
|
|
201
|
+
return { content: [{ type: 'text', text: JSON.stringify({ results, count: results.length }, null, 2) }] };
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'wasm_gallery_create',
|
|
210
|
+
description: 'Create a WASM agent from a gallery template.',
|
|
211
|
+
inputSchema: {
|
|
212
|
+
type: 'object',
|
|
213
|
+
properties: {
|
|
214
|
+
template: { type: 'string', description: 'Template name (coder, researcher, tester, reviewer, security, swarm)' },
|
|
215
|
+
},
|
|
216
|
+
required: ['template'],
|
|
217
|
+
},
|
|
218
|
+
handler: async (args) => {
|
|
219
|
+
try {
|
|
220
|
+
const wasm = await loadAgentWasm();
|
|
221
|
+
const info = await wasm.createAgentFromTemplate(args.template);
|
|
222
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: true, agent: info, template: args.template }, null, 2) }] };
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
];
|
|
230
|
+
//# sourceMappingURL=wasm-agent-tools.js.map
|
|
@@ -1315,6 +1315,60 @@ export async function loadEmbeddingModel(options) {
|
|
|
1315
1315
|
loadTime: Date.now() - startTime
|
|
1316
1316
|
};
|
|
1317
1317
|
}
|
|
1318
|
+
// Fallback: Check for ruvector ONNX embedder (bundled MiniLM-L6-v2 since v0.2.15)
|
|
1319
|
+
// v0.2.16: LoRA B=0 fix makes AdaptiveEmbedder safe (identity when untrained)
|
|
1320
|
+
const ruvector = await import('ruvector').catch(() => null);
|
|
1321
|
+
if (ruvector?.initOnnxEmbedder) {
|
|
1322
|
+
try {
|
|
1323
|
+
await ruvector.initOnnxEmbedder();
|
|
1324
|
+
// Prefer AdaptiveEmbedder (LoRA adaptation capability, identity when untrained)
|
|
1325
|
+
if (ruvector.AdaptiveEmbedder) {
|
|
1326
|
+
try {
|
|
1327
|
+
const adaptive = new ruvector.AdaptiveEmbedder({ useEpisodic: false });
|
|
1328
|
+
if (adaptive?.embed && adaptive.isReady?.()) {
|
|
1329
|
+
if (verbose) {
|
|
1330
|
+
console.log('Loading ruvector AdaptiveEmbedder (all-MiniLM-L6-v2 + LoRA)...');
|
|
1331
|
+
}
|
|
1332
|
+
embeddingModelState = {
|
|
1333
|
+
loaded: true,
|
|
1334
|
+
model: (text) => adaptive.embed(text),
|
|
1335
|
+
tokenizer: null,
|
|
1336
|
+
dimensions: adaptive.getDimension?.() || 384
|
|
1337
|
+
};
|
|
1338
|
+
return {
|
|
1339
|
+
success: true,
|
|
1340
|
+
dimensions: adaptive.getDimension?.() || 384,
|
|
1341
|
+
modelName: 'ruvector/adaptive',
|
|
1342
|
+
loadTime: Date.now() - startTime
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
catch { /* fall through to OptimizedOnnxEmbedder */ }
|
|
1347
|
+
}
|
|
1348
|
+
// Fallback: OptimizedOnnxEmbedder (raw ONNX without LoRA)
|
|
1349
|
+
const onnxEmb = ruvector.getOptimizedOnnxEmbedder?.();
|
|
1350
|
+
if (onnxEmb?.embed && onnxEmb.isReady?.()) {
|
|
1351
|
+
if (verbose) {
|
|
1352
|
+
console.log('Loading ruvector ONNX embedder (all-MiniLM-L6-v2)...');
|
|
1353
|
+
}
|
|
1354
|
+
embeddingModelState = {
|
|
1355
|
+
loaded: true,
|
|
1356
|
+
model: (text) => onnxEmb.embed(text),
|
|
1357
|
+
tokenizer: null,
|
|
1358
|
+
dimensions: onnxEmb.getDimension?.() || 384
|
|
1359
|
+
};
|
|
1360
|
+
return {
|
|
1361
|
+
success: true,
|
|
1362
|
+
dimensions: onnxEmb.getDimension?.() || 384,
|
|
1363
|
+
modelName: 'ruvector/onnx',
|
|
1364
|
+
loadTime: Date.now() - startTime
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
catch {
|
|
1369
|
+
// ruvector ONNX init failed, continue to next fallback
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1318
1372
|
// Legacy fallback: Check for agentic-flow core embeddings
|
|
1319
1373
|
const agenticFlow = await import('agentic-flow').catch(() => null);
|
|
1320
1374
|
if (agenticFlow && agenticFlow.embeddings) {
|
|
@@ -1378,12 +1432,17 @@ export async function generateEmbedding(text) {
|
|
|
1378
1432
|
if (state.model && typeof state.model === 'function') {
|
|
1379
1433
|
try {
|
|
1380
1434
|
const output = await state.model(text, { pooling: 'mean', normalize: true });
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1435
|
+
// Handle both @xenova/transformers (output.data) and ruvector (plain array) formats
|
|
1436
|
+
const embedding = output?.data
|
|
1437
|
+
? Array.from(output.data)
|
|
1438
|
+
: Array.isArray(output) ? output : null;
|
|
1439
|
+
if (embedding) {
|
|
1440
|
+
return {
|
|
1441
|
+
embedding,
|
|
1442
|
+
dimensions: embedding.length,
|
|
1443
|
+
model: 'onnx'
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1387
1446
|
}
|
|
1388
1447
|
catch {
|
|
1389
1448
|
// Fall through to fallback
|