@kernel.chat/kbot 3.69.1 → 3.70.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/a2a.d.ts +50 -5
- package/dist/a2a.js +305 -44
- package/dist/auth.d.ts +4 -0
- package/dist/auth.js +43 -4
- package/dist/doctor.js +53 -7
- package/dist/machine.d.ts +1 -0
- package/dist/machine.js +17 -1
- package/dist/serve.js +3 -2
- package/dist/tools/a2a.d.ts +2 -0
- package/dist/tools/a2a.js +233 -0
- package/dist/tools/ai-analysis.d.ts +2 -0
- package/dist/tools/ai-analysis.js +677 -0
- package/dist/tools/financial-analysis.d.ts +2 -0
- package/dist/tools/financial-analysis.js +945 -0
- package/dist/tools/index.js +5 -0
- package/dist/tools/music-gen.d.ts +2 -0
- package/dist/tools/music-gen.js +1006 -0
- package/dist/tools/threat-intel.d.ts +2 -0
- package/dist/tools/threat-intel.js +1619 -0
- package/package.json +2 -2
package/dist/doctor.js
CHANGED
|
@@ -9,7 +9,7 @@ import { join } from 'node:path';
|
|
|
9
9
|
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
10
10
|
import { execSync } from 'node:child_process';
|
|
11
11
|
import chalk from 'chalk';
|
|
12
|
-
import { loadConfig, isByokEnabled, getByokProvider, isLocalProvider, PROVIDERS, KBOT_DIR, ENV_KEYS, } from './auth.js';
|
|
12
|
+
import { loadConfig, isByokEnabled, getByokProvider, isLocalProvider, PROVIDERS, KBOT_DIR, ENV_KEYS, getOllamaVersion, isOllamaMLXBackend, } from './auth.js';
|
|
13
13
|
import { getExtendedStats } from './learning.js';
|
|
14
14
|
import { getMachineProfile, probeMachine } from './machine.js';
|
|
15
15
|
import { createRequire } from 'node:module';
|
|
@@ -168,15 +168,25 @@ async function checkLocalProviderReachable(providerId, providerConfig) {
|
|
|
168
168
|
if (!res.ok) {
|
|
169
169
|
return { name: providerConfig.name, status: 'fail', message: `returned ${res.status}` };
|
|
170
170
|
}
|
|
171
|
-
// For Ollama, report model count
|
|
171
|
+
// For Ollama, report model count, version, and MLX backend
|
|
172
172
|
if (providerId === 'ollama') {
|
|
173
173
|
try {
|
|
174
174
|
const data = await res.json();
|
|
175
175
|
const models = data.models ?? [];
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
176
|
+
const parts = [];
|
|
177
|
+
// Model count
|
|
178
|
+
parts.push(models.length > 0
|
|
179
|
+
? `${models.length} model${models.length !== 1 ? 's' : ''}`
|
|
180
|
+
: 'no models pulled');
|
|
181
|
+
// Version
|
|
182
|
+
const version = await getOllamaVersion();
|
|
183
|
+
if (version)
|
|
184
|
+
parts.push(`v${version}`);
|
|
185
|
+
// MLX backend detection
|
|
186
|
+
const mlxActive = await isOllamaMLXBackend();
|
|
187
|
+
if (mlxActive)
|
|
188
|
+
parts.push('MLX backend');
|
|
189
|
+
return { name: providerConfig.name, status: 'pass', message: `running (${parts.join(', ')})` };
|
|
180
190
|
}
|
|
181
191
|
catch {
|
|
182
192
|
return { name: providerConfig.name, status: 'pass', message: 'running' };
|
|
@@ -362,6 +372,38 @@ function checkShell() {
|
|
|
362
372
|
const completionStatus = completionsInstalled ? ', completions installed' : '';
|
|
363
373
|
return { name: 'Shell', status: 'pass', message: `${shellName}${completionStatus}` };
|
|
364
374
|
}
|
|
375
|
+
// ── Ollama MLX check (0.19+ on Apple Silicon) ──
|
|
376
|
+
async function checkOllamaMLX() {
|
|
377
|
+
// Only relevant on Apple Silicon
|
|
378
|
+
if (process.platform !== 'darwin' || process.arch !== 'arm64')
|
|
379
|
+
return null;
|
|
380
|
+
const version = await getOllamaVersion();
|
|
381
|
+
if (!version)
|
|
382
|
+
return null; // Ollama not running — already reported by provider check
|
|
383
|
+
const parts = version.split('.').map(Number);
|
|
384
|
+
const [major = 0, minor = 0] = parts;
|
|
385
|
+
if (major > 0 || minor >= 19) {
|
|
386
|
+
const mlxActive = await isOllamaMLXBackend();
|
|
387
|
+
if (mlxActive) {
|
|
388
|
+
return {
|
|
389
|
+
name: 'Ollama MLX',
|
|
390
|
+
status: 'pass',
|
|
391
|
+
message: `Ollama ${version} — MLX backend available for 2x faster inference on Apple Silicon`,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
return {
|
|
395
|
+
name: 'Ollama MLX',
|
|
396
|
+
status: 'pass',
|
|
397
|
+
message: `Ollama ${version} — MLX supported (57% faster prefill, 93% faster decode on Apple Silicon)`,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
// Ollama is running but older than 0.19
|
|
401
|
+
return {
|
|
402
|
+
name: 'Ollama MLX',
|
|
403
|
+
status: 'warn',
|
|
404
|
+
message: `Ollama ${version} — upgrade to 0.19+ for MLX backend (2x faster on Apple Silicon)`,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
365
407
|
// ── Hardware checks (uses machine.ts) ──
|
|
366
408
|
async function checkHardware() {
|
|
367
409
|
const results = [];
|
|
@@ -435,16 +477,20 @@ export async function runDoctor() {
|
|
|
435
477
|
checks.push(checkKbotVersion());
|
|
436
478
|
checks.push(checkApiKey());
|
|
437
479
|
// Async checks — run in parallel for speed
|
|
438
|
-
const [providerResults, hardwareResults] = await Promise.all([
|
|
480
|
+
const [providerResults, hardwareResults, ollamaMLXResult] = await Promise.all([
|
|
439
481
|
checkAllProviders().catch(() => [
|
|
440
482
|
{ name: 'Providers', status: 'warn', message: 'check failed unexpectedly' },
|
|
441
483
|
]),
|
|
442
484
|
checkHardware().catch(() => [
|
|
443
485
|
{ name: 'Hardware', status: 'warn', message: 'check failed unexpectedly' },
|
|
444
486
|
]),
|
|
487
|
+
checkOllamaMLX().catch(() => null),
|
|
445
488
|
]);
|
|
446
489
|
// Provider checks (one line per provider)
|
|
447
490
|
checks.push(...providerResults);
|
|
491
|
+
// Ollama MLX check (Apple Silicon only)
|
|
492
|
+
if (ollamaMLXResult)
|
|
493
|
+
checks.push(ollamaMLXResult);
|
|
448
494
|
// Hardware checks
|
|
449
495
|
checks.push(...hardwareResults);
|
|
450
496
|
// More synchronous checks
|
package/dist/machine.d.ts
CHANGED
|
@@ -73,6 +73,7 @@ export interface MachineProfile {
|
|
|
73
73
|
canRunLocalModels: boolean;
|
|
74
74
|
gpuAcceleration: 'metal' | 'cuda' | 'vulkan' | 'cpu-only';
|
|
75
75
|
recommendedModelSize: string;
|
|
76
|
+
mlxAvailable: boolean;
|
|
76
77
|
probedAt: string;
|
|
77
78
|
}
|
|
78
79
|
export declare function probeMachine(): Promise<MachineProfile>;
|
package/dist/machine.js
CHANGED
|
@@ -314,6 +314,18 @@ function detectGpuAcceleration(gpus) {
|
|
|
314
314
|
return 'vulkan';
|
|
315
315
|
return 'cpu-only';
|
|
316
316
|
}
|
|
317
|
+
// ── MLX framework detection (Apple Silicon) ──
|
|
318
|
+
function detectMLX() {
|
|
319
|
+
if (platform() !== 'darwin' || arch() !== 'arm64')
|
|
320
|
+
return false;
|
|
321
|
+
// Quick check: try importing mlx in Python
|
|
322
|
+
const result = exec('python3 -c "import mlx; print(mlx.__version__)" 2>/dev/null', 3000);
|
|
323
|
+
if (result && !result.includes('ModuleNotFoundError'))
|
|
324
|
+
return true;
|
|
325
|
+
// Fallback: check common pip install path
|
|
326
|
+
const globCheck = exec('ls /usr/local/lib/python3.*/site-packages/mlx/__init__.py 2>/dev/null || ls ~/Library/Python/3.*/lib/python/site-packages/mlx/__init__.py 2>/dev/null', 2000);
|
|
327
|
+
return !!globCheck;
|
|
328
|
+
}
|
|
317
329
|
// ── Model size recommendation ──
|
|
318
330
|
function recommendModelSize(totalMemoryGB, gpuAccel) {
|
|
319
331
|
// Conservative: leave room for OS + apps
|
|
@@ -374,6 +386,7 @@ export async function probeMachine() {
|
|
|
374
386
|
osInfo = probeLinuxOs();
|
|
375
387
|
}
|
|
376
388
|
const gpuAccel = detectGpuAcceleration(gpus);
|
|
389
|
+
const mlxAvailable = detectMLX();
|
|
377
390
|
const devTools = probeDevTools();
|
|
378
391
|
// Uptime
|
|
379
392
|
const uptimeSeconds = plat === 'darwin'
|
|
@@ -420,6 +433,7 @@ export async function probeMachine() {
|
|
|
420
433
|
canRunLocalModels: gpuAccel !== 'cpu-only' || totalGB >= 8,
|
|
421
434
|
gpuAcceleration: gpuAccel,
|
|
422
435
|
recommendedModelSize: recommendModelSize(totalGB, gpuAccel),
|
|
436
|
+
mlxAvailable,
|
|
423
437
|
probedAt: new Date().toISOString(),
|
|
424
438
|
};
|
|
425
439
|
cached = profile;
|
|
@@ -505,6 +519,8 @@ export function formatMachineProfile(p) {
|
|
|
505
519
|
lines.push('');
|
|
506
520
|
lines.push(' AI Capabilities');
|
|
507
521
|
lines.push(` Acceleration ${p.gpuAcceleration}`);
|
|
522
|
+
if (p.mlxAvailable)
|
|
523
|
+
lines.push(` MLX framework available (Apple Silicon accelerated)`);
|
|
508
524
|
lines.push(` Local models ${p.canRunLocalModels ? 'yes' : 'no'}`);
|
|
509
525
|
lines.push(` Recommended up to ${p.recommendedModelSize} parameters`);
|
|
510
526
|
lines.push('');
|
|
@@ -529,7 +545,7 @@ export function formatMachineForPrompt(p) {
|
|
|
529
545
|
if (p.battery.present) {
|
|
530
546
|
parts.push(`Battery: ${p.battery.percent}% ${p.battery.charging ? 'charging' : 'discharging'}`);
|
|
531
547
|
}
|
|
532
|
-
parts.push(`GPU accel: ${p.gpuAcceleration} — local models up to ${p.recommendedModelSize}`);
|
|
548
|
+
parts.push(`GPU accel: ${p.gpuAcceleration}${p.mlxAvailable ? ' + MLX' : ''} — local models up to ${p.recommendedModelSize}`);
|
|
533
549
|
const toolNames = p.devTools.map(t => `${t.name} ${t.version}`).join(', ');
|
|
534
550
|
if (toolNames)
|
|
535
551
|
parts.push(`Tools: ${toolNames}`);
|
package/dist/serve.js
CHANGED
|
@@ -235,8 +235,9 @@ export async function startServe(options) {
|
|
|
235
235
|
printInfo(` GET /metrics — Execution metrics`);
|
|
236
236
|
printInfo(` GET /apps — List MCP App-capable tools`);
|
|
237
237
|
printInfo(` GET /.well-known/agent.json — A2A Agent Card`);
|
|
238
|
-
printInfo(` POST /a2a
|
|
239
|
-
printInfo(`
|
|
238
|
+
printInfo(` POST /a2a — A2A JSON-RPC endpoint`);
|
|
239
|
+
printInfo(` POST /a2a/tasks — A2A submit task (REST)`);
|
|
240
|
+
printInfo(` GET /a2a/tasks/:id — A2A task status (REST)`);
|
|
240
241
|
if (options.token) {
|
|
241
242
|
printInfo(` Auth: Bearer token required`);
|
|
242
243
|
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// kbot A2A Tools — Agent-to-Agent protocol status and management
|
|
2
|
+
//
|
|
3
|
+
// Provides the a2a_status tool for inspecting the A2A server state,
|
|
4
|
+
// registered capabilities, active tasks, and discovered remote agents.
|
|
5
|
+
//
|
|
6
|
+
// Tools: a2a_status, a2a_discover, a2a_send, a2a_card
|
|
7
|
+
import { registerTool } from './index.js';
|
|
8
|
+
import { getA2AStatus, buildAgentCard, discoverAgent, delegateTask, listRemoteAgents, removeRemoteAgent, } from '../a2a.js';
|
|
9
|
+
export function registerA2ATools() {
|
|
10
|
+
// ── a2a_status ──
|
|
11
|
+
registerTool({
|
|
12
|
+
name: 'a2a_status',
|
|
13
|
+
description: 'Show A2A (Agent-to-Agent) protocol server status: whether the server is running, ' +
|
|
14
|
+
'registered agent capabilities (all 35 kbot specialists), task statistics, ' +
|
|
15
|
+
'active connections, and discovered remote agents.',
|
|
16
|
+
parameters: {
|
|
17
|
+
verbose: {
|
|
18
|
+
type: 'boolean',
|
|
19
|
+
description: 'Show full skill descriptions and all tags. Defaults to false (summary only).',
|
|
20
|
+
required: false,
|
|
21
|
+
default: false,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
tier: 'free',
|
|
25
|
+
async execute(args) {
|
|
26
|
+
const verbose = args.verbose === true || args.verbose === 'true';
|
|
27
|
+
const status = getA2AStatus();
|
|
28
|
+
const lines = [];
|
|
29
|
+
// Server section
|
|
30
|
+
lines.push('=== A2A Server Status ===');
|
|
31
|
+
lines.push(`Running: ${status.server.running ? 'YES' : 'NO'}`);
|
|
32
|
+
if (status.server.running) {
|
|
33
|
+
lines.push(`Endpoint: ${status.server.endpointUrl}`);
|
|
34
|
+
lines.push(`Started: ${status.server.startedAt}`);
|
|
35
|
+
lines.push(`Uptime: ${status.server.uptime}`);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
lines.push('(Server not started. Run `kbot serve` to start the A2A endpoint.)');
|
|
39
|
+
}
|
|
40
|
+
// Tasks section
|
|
41
|
+
lines.push('');
|
|
42
|
+
lines.push('=== Task Statistics ===');
|
|
43
|
+
lines.push(`Received: ${status.tasks.received}`);
|
|
44
|
+
lines.push(`Completed: ${status.tasks.completed}`);
|
|
45
|
+
lines.push(`Failed: ${status.tasks.failed}`);
|
|
46
|
+
lines.push(`Active: ${status.tasks.active}`);
|
|
47
|
+
lines.push(`In Store: ${status.tasks.stored}`);
|
|
48
|
+
// Capabilities section
|
|
49
|
+
lines.push('');
|
|
50
|
+
lines.push(`=== Registered Capabilities (${status.capabilities.totalSkills} agents) ===`);
|
|
51
|
+
if (verbose) {
|
|
52
|
+
for (const skill of status.capabilities.skills) {
|
|
53
|
+
lines.push(` ${skill.id}: ${skill.name}`);
|
|
54
|
+
lines.push(` Tags: ${skill.tags.join(', ')}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// Compact: group by category
|
|
59
|
+
const ids = status.capabilities.skills.map(s => s.id);
|
|
60
|
+
lines.push(`Agents: ${ids.join(', ')}`);
|
|
61
|
+
}
|
|
62
|
+
// Connections section
|
|
63
|
+
lines.push('');
|
|
64
|
+
lines.push(`=== Active Connections (${status.connections.uniqueClients} unique clients) ===`);
|
|
65
|
+
if (status.connections.clients.length === 0) {
|
|
66
|
+
lines.push(' (no connections yet)');
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
for (const client of status.connections.clients) {
|
|
70
|
+
lines.push(` - ${client}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Remote agents section
|
|
74
|
+
lines.push('');
|
|
75
|
+
lines.push(`=== Discovered Remote Agents (${status.registry.remoteAgents}) ===`);
|
|
76
|
+
if (status.registry.agents.length === 0) {
|
|
77
|
+
lines.push(' (none discovered — use a2a_discover to find remote agents)');
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
for (const agent of status.registry.agents) {
|
|
81
|
+
lines.push(` - ${agent.name} @ ${agent.url} (${agent.skills} skills)`);
|
|
82
|
+
if (agent.lastContact) {
|
|
83
|
+
lines.push(` Last contact: ${agent.lastContact}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return lines.join('\n');
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
// ── a2a_discover ──
|
|
91
|
+
registerTool({
|
|
92
|
+
name: 'a2a_discover',
|
|
93
|
+
description: 'Discover a remote A2A agent by URL. Fetches its Agent Card from ' +
|
|
94
|
+
'<url>/.well-known/agent.json and registers it in the local registry ' +
|
|
95
|
+
'for future task delegation.',
|
|
96
|
+
parameters: {
|
|
97
|
+
url: {
|
|
98
|
+
type: 'string',
|
|
99
|
+
description: 'Base URL of the remote agent (e.g. "http://other-agent:8080")',
|
|
100
|
+
required: true,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
tier: 'free',
|
|
104
|
+
async execute(args) {
|
|
105
|
+
const url = String(args.url).trim();
|
|
106
|
+
if (!url)
|
|
107
|
+
return 'Error: url is required';
|
|
108
|
+
const card = await discoverAgent(url);
|
|
109
|
+
if (!card) {
|
|
110
|
+
return `Failed to discover agent at ${url}. Ensure the agent is running and exposes /.well-known/agent.json.`;
|
|
111
|
+
}
|
|
112
|
+
const lines = [
|
|
113
|
+
`Discovered: ${card.name} v${card.version}`,
|
|
114
|
+
`URL: ${card.url}`,
|
|
115
|
+
`Provider: ${card.provider.organization}`,
|
|
116
|
+
`Skills (${card.skills.length}):`,
|
|
117
|
+
...card.skills.map(s => ` - ${s.id}: ${s.name} [${s.tags.join(', ')}]`),
|
|
118
|
+
'',
|
|
119
|
+
'Agent registered in local registry. Use a2a_send to delegate tasks.',
|
|
120
|
+
];
|
|
121
|
+
return lines.join('\n');
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
// ── a2a_send ──
|
|
125
|
+
registerTool({
|
|
126
|
+
name: 'a2a_send',
|
|
127
|
+
description: 'Send a task to a remote A2A agent. Delegates work to another agent\'s ' +
|
|
128
|
+
'specialist via the A2A protocol. Optionally specify which agent skill to use.',
|
|
129
|
+
parameters: {
|
|
130
|
+
url: {
|
|
131
|
+
type: 'string',
|
|
132
|
+
description: 'Base URL of the remote agent',
|
|
133
|
+
required: true,
|
|
134
|
+
},
|
|
135
|
+
task: {
|
|
136
|
+
type: 'string',
|
|
137
|
+
description: 'The task prompt to send to the remote agent',
|
|
138
|
+
required: true,
|
|
139
|
+
},
|
|
140
|
+
agent: {
|
|
141
|
+
type: 'string',
|
|
142
|
+
description: 'Hint which specialist agent should handle the task (optional)',
|
|
143
|
+
required: false,
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
tier: 'free',
|
|
147
|
+
timeout: 180_000, // 3 minutes for remote task execution
|
|
148
|
+
async execute(args) {
|
|
149
|
+
const url = String(args.url).trim();
|
|
150
|
+
const task = String(args.task).trim();
|
|
151
|
+
const agent = args.agent ? String(args.agent).trim() : undefined;
|
|
152
|
+
if (!url || !task)
|
|
153
|
+
return 'Error: url and task are required';
|
|
154
|
+
const result = await delegateTask(url, task, { agent });
|
|
155
|
+
if (!result) {
|
|
156
|
+
return `Task delegation to ${url} failed. The remote agent may be down or the task execution failed.`;
|
|
157
|
+
}
|
|
158
|
+
return [
|
|
159
|
+
`=== Task Result from ${url} ===`,
|
|
160
|
+
result.text,
|
|
161
|
+
'',
|
|
162
|
+
result.metadata.agentUsed ? `Agent used: ${result.metadata.agentUsed}` : '',
|
|
163
|
+
result.metadata.model ? `Model: ${result.metadata.model}` : '',
|
|
164
|
+
].filter(Boolean).join('\n');
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
// ── a2a_card ──
|
|
168
|
+
registerTool({
|
|
169
|
+
name: 'a2a_card',
|
|
170
|
+
description: 'Generate and display kbot\'s A2A Agent Card — the JSON descriptor that ' +
|
|
171
|
+
'other agents use to discover kbot\'s capabilities. Shows all 35 specialist ' +
|
|
172
|
+
'agents as skills with tags and descriptions.',
|
|
173
|
+
parameters: {
|
|
174
|
+
url: {
|
|
175
|
+
type: 'string',
|
|
176
|
+
description: 'Override the endpoint URL in the card (defaults to the current server URL)',
|
|
177
|
+
required: false,
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
tier: 'free',
|
|
181
|
+
async execute(args) {
|
|
182
|
+
const url = args.url ? String(args.url).trim() : undefined;
|
|
183
|
+
const card = buildAgentCard(url);
|
|
184
|
+
return JSON.stringify(card, null, 2);
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
// ── a2a_remove ──
|
|
188
|
+
registerTool({
|
|
189
|
+
name: 'a2a_remove',
|
|
190
|
+
description: 'Remove a remote A2A agent from the local discovery registry.',
|
|
191
|
+
parameters: {
|
|
192
|
+
url: {
|
|
193
|
+
type: 'string',
|
|
194
|
+
description: 'Base URL of the remote agent to remove',
|
|
195
|
+
required: true,
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
tier: 'free',
|
|
199
|
+
async execute(args) {
|
|
200
|
+
const url = String(args.url).trim();
|
|
201
|
+
if (!url)
|
|
202
|
+
return 'Error: url is required';
|
|
203
|
+
const removed = removeRemoteAgent(url);
|
|
204
|
+
return removed
|
|
205
|
+
? `Removed ${url} from the A2A registry.`
|
|
206
|
+
: `Agent ${url} not found in the registry.`;
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
// ── a2a_list ──
|
|
210
|
+
registerTool({
|
|
211
|
+
name: 'a2a_list',
|
|
212
|
+
description: 'List all discovered remote A2A agents from the local registry.',
|
|
213
|
+
parameters: {},
|
|
214
|
+
tier: 'free',
|
|
215
|
+
async execute() {
|
|
216
|
+
const agents = listRemoteAgents();
|
|
217
|
+
if (agents.length === 0) {
|
|
218
|
+
return 'No remote agents discovered. Use a2a_discover to find agents.';
|
|
219
|
+
}
|
|
220
|
+
const lines = agents.map(a => {
|
|
221
|
+
const skills = a.card.skills.map(s => s.id).join(', ');
|
|
222
|
+
return [
|
|
223
|
+
`${a.card.name} (${a.url})`,
|
|
224
|
+
` Skills: ${skills}`,
|
|
225
|
+
` Discovered: ${a.discoveredAt}`,
|
|
226
|
+
a.lastContactedAt ? ` Last contact: ${a.lastContactedAt}` : '',
|
|
227
|
+
].filter(Boolean).join('\n');
|
|
228
|
+
});
|
|
229
|
+
return `=== Discovered Remote Agents (${agents.length}) ===\n\n${lines.join('\n\n')}`;
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=a2a.js.map
|