@elisym/mcp 0.8.11 → 0.8.13
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 +94 -47
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { LIMITS, DEFAULT_KIND_OFFSET, SolanaPaymentStrategy, makeCensor, DEFAULT_REDACT_PATHS, validateAgentName, RELAYS, toDTag, formatAssetAmount, USDC_SOLANA_DEVNET, estimateSolFeeLamports, formatFeeBreakdown, resolveAssetFromPaymentRequest as resolveAssetFromPaymentRequest$1, parseAssetAmount, ElisymIdentity, ElisymClient, NATIVE_SOL, resolveKnownAsset,
|
|
2
|
+
import { LIMITS, DEFAULT_KIND_OFFSET, SolanaPaymentStrategy, makeCensor, DEFAULT_REDACT_PATHS, validateAgentName, RELAYS, toDTag, formatAssetAmount, estimateNetworkBaseline, formatSol as formatSol$1, USDC_SOLANA_DEVNET, estimateSolFeeLamports, formatFeeBreakdown, resolveAssetFromPaymentRequest as resolveAssetFromPaymentRequest$1, parseAssetAmount, ElisymIdentity, ElisymClient, NATIVE_SOL, resolveKnownAsset, formatNetworkBaseline, getProtocolProgramId, getProtocolConfig, assetKey, assetByKey, KNOWN_ASSETS } from '@elisym/sdk';
|
|
3
3
|
import { listAgents, createAgentDir, writeYaml, writeSecrets, resolveAgent, loadAgent, globalConfigPath, writeFileAtomic as writeFileAtomic$1 } from '@elisym/sdk/agent-store';
|
|
4
4
|
import { loadGlobalConfig, writeGlobalConfig } from '@elisym/sdk/node';
|
|
5
|
-
import { getBase58Encoder, getBase58Decoder, generateKeyPairSigner, address, createSolanaRpcSubscriptions, sendAndConfirmTransactionFactory, getSignatureFromTransaction, pipe, createTransactionMessage, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions, signTransactionMessageWithSigners, createKeyPairSignerFromBytes,
|
|
5
|
+
import { getBase58Encoder, getBase58Decoder, generateKeyPairSigner, createSolanaRpc, address, createSolanaRpcSubscriptions, sendAndConfirmTransactionFactory, getSignatureFromTransaction, pipe, createTransactionMessage, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions, signTransactionMessageWithSigners, createKeyPairSignerFromBytes, isAddress } from '@solana/kit';
|
|
6
6
|
import { Command } from 'commander';
|
|
7
7
|
import { generateSecretKey, nip19, getPublicKey } from 'nostr-tools';
|
|
8
8
|
import { readFile, writeFile, rename, unlink } from 'node:fs/promises';
|
|
@@ -2095,14 +2095,13 @@ var ContactSchema = z.object({
|
|
|
2095
2095
|
name: z.string().max(200).optional(),
|
|
2096
2096
|
addedAt: z.number().int().nonnegative(),
|
|
2097
2097
|
lastJobAt: z.number().int().nonnegative().optional(),
|
|
2098
|
-
jobCount: z.number().int().nonnegative(),
|
|
2099
2098
|
lastCapability: z.string().max(200).optional(),
|
|
2100
2099
|
note: z.string().max(500).optional()
|
|
2101
|
-
})
|
|
2100
|
+
});
|
|
2102
2101
|
var ContactsSchema = z.object({
|
|
2103
2102
|
version: z.literal(1),
|
|
2104
2103
|
contacts: z.array(ContactSchema)
|
|
2105
|
-
})
|
|
2104
|
+
});
|
|
2106
2105
|
var EMPTY2 = { version: 1, contacts: [] };
|
|
2107
2106
|
var writeLocks2 = /* @__PURE__ */ new Map();
|
|
2108
2107
|
function withLock2(path, fn) {
|
|
@@ -2157,8 +2156,7 @@ async function upsertContact(agentDir, input) {
|
|
|
2157
2156
|
name: input.name ?? existing.name,
|
|
2158
2157
|
note: input.note ?? existing.note,
|
|
2159
2158
|
lastJobAt: input.lastJobAt ?? existing.lastJobAt,
|
|
2160
|
-
lastCapability: input.lastCapability ?? existing.lastCapability
|
|
2161
|
-
jobCount: input.jobCount ?? existing.jobCount + 1
|
|
2159
|
+
lastCapability: input.lastCapability ?? existing.lastCapability
|
|
2162
2160
|
});
|
|
2163
2161
|
data.contacts[index] = merged;
|
|
2164
2162
|
} else {
|
|
@@ -2169,8 +2167,7 @@ async function upsertContact(agentDir, input) {
|
|
|
2169
2167
|
note: input.note,
|
|
2170
2168
|
addedAt: Date.now(),
|
|
2171
2169
|
lastJobAt: input.lastJobAt,
|
|
2172
|
-
lastCapability: input.lastCapability
|
|
2173
|
-
jobCount: input.jobCount ?? 0
|
|
2170
|
+
lastCapability: input.lastCapability
|
|
2174
2171
|
});
|
|
2175
2172
|
data.contacts.push(merged);
|
|
2176
2173
|
}
|
|
@@ -2339,7 +2336,7 @@ var GetIdentitySchema = z.object({});
|
|
|
2339
2336
|
var discoveryTools = [
|
|
2340
2337
|
defineTool({
|
|
2341
2338
|
name: "search_agents",
|
|
2342
|
-
description: "Search AI agents currently online on elisym. `capabilities` is a hard OR-filter of substring tokens from the user's request (never invent synonyms). `query` is optional re-ranking; omit if not needed. Offline agents are excluded by default - pass include_offline=true only when debugging.",
|
|
2339
|
+
description: "Search AI agents currently online on elisym. `capabilities` is a hard OR-filter of substring tokens from the user's request (never invent synonyms). `query` is optional re-ranking; omit if not needed. Offline agents are excluded by default - pass include_offline=true only when debugging. Results that match a saved contact are sorted to the top and annotated with `is_contact`, `last_worked_at`, `last_capability`, and `contact_note` - surface this to the user (e.g. \"already in your contacts, last used <date>\") so they can prefer providers they've worked with before.",
|
|
2343
2340
|
schema: SearchAgentsSchema,
|
|
2344
2341
|
async handler(ctx, input) {
|
|
2345
2342
|
const { capabilities, query, max_price_lamports, include_offline, contacts_only } = input;
|
|
@@ -2347,25 +2344,27 @@ var discoveryTools = [
|
|
|
2347
2344
|
return errorResult(`Too many capabilities (max ${MAX_CAPABILITIES})`);
|
|
2348
2345
|
}
|
|
2349
2346
|
const agent = ctx.active();
|
|
2350
|
-
|
|
2347
|
+
const contactByPubkey = /* @__PURE__ */ new Map();
|
|
2348
|
+
if (agent.agentDir) {
|
|
2349
|
+
const data = await readContacts(agent.agentDir);
|
|
2350
|
+
for (const contact of data.contacts) {
|
|
2351
|
+
contactByPubkey.set(contact.pubkey, contact);
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2351
2354
|
if (contacts_only) {
|
|
2352
2355
|
if (!agent.agentDir) {
|
|
2353
2356
|
return textResult(
|
|
2354
2357
|
"contacts_only=true requires a persistent agent (no on-disk directory for the active agent)."
|
|
2355
2358
|
);
|
|
2356
2359
|
}
|
|
2357
|
-
|
|
2358
|
-
if (data.contacts.length === 0) {
|
|
2360
|
+
if (contactByPubkey.size === 0) {
|
|
2359
2361
|
return textResult(
|
|
2360
2362
|
"No contacts saved yet. Use add_contact (or rate a job positively with submit_feedback and then add_contact) before searching with contacts_only=true."
|
|
2361
2363
|
);
|
|
2362
2364
|
}
|
|
2363
|
-
lastWorkedAtByPubkey = new Map(
|
|
2364
|
-
data.contacts.map((contact) => [contact.pubkey, contact.lastJobAt ?? contact.addedAt])
|
|
2365
|
-
);
|
|
2366
2365
|
}
|
|
2367
2366
|
const agents = await agent.client.discovery.fetchAgents(agent.network);
|
|
2368
|
-
let filtered = contacts_only ? agents.filter((a) =>
|
|
2367
|
+
let filtered = contacts_only ? agents.filter((a) => contactByPubkey.has(a.pubkey)) : agents;
|
|
2369
2368
|
filtered = filtered.filter(
|
|
2370
2369
|
(a) => a.cards.some(
|
|
2371
2370
|
(card) => capabilities.some(
|
|
@@ -2408,38 +2407,89 @@ var discoveryTools = [
|
|
|
2408
2407
|
hits++;
|
|
2409
2408
|
}
|
|
2410
2409
|
}
|
|
2411
|
-
|
|
2410
|
+
const contactBoost = contactByPubkey.has(a.pubkey) ? 0.5 : 0;
|
|
2411
|
+
return { agent: a, score: hits + contactBoost };
|
|
2412
2412
|
});
|
|
2413
2413
|
filtered = scored.filter((s) => s.score > 0).sort((a, b) => b.score - a.score).map((s) => s.agent);
|
|
2414
2414
|
}
|
|
2415
|
+
} else if (contactByPubkey.size > 0) {
|
|
2416
|
+
filtered = [...filtered].sort((left, right) => {
|
|
2417
|
+
const leftContact = contactByPubkey.get(left.pubkey);
|
|
2418
|
+
const rightContact = contactByPubkey.get(right.pubkey);
|
|
2419
|
+
if (leftContact && !rightContact) {
|
|
2420
|
+
return -1;
|
|
2421
|
+
}
|
|
2422
|
+
if (rightContact && !leftContact) {
|
|
2423
|
+
return 1;
|
|
2424
|
+
}
|
|
2425
|
+
if (leftContact && rightContact) {
|
|
2426
|
+
const leftTs = leftContact.lastJobAt ?? leftContact.addedAt;
|
|
2427
|
+
const rightTs = rightContact.lastJobAt ?? rightContact.addedAt;
|
|
2428
|
+
return rightTs - leftTs;
|
|
2429
|
+
}
|
|
2430
|
+
return 0;
|
|
2431
|
+
});
|
|
2415
2432
|
}
|
|
2416
2433
|
if (filtered.length === 0) {
|
|
2417
2434
|
return textResult(
|
|
2418
2435
|
include_offline ? "No agents found matching those capabilities." : "No online agents found matching those capabilities. Retry shortly or pass include_offline=true to see unreachable matches."
|
|
2419
2436
|
);
|
|
2420
2437
|
}
|
|
2421
|
-
const
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2438
|
+
const needsAtaSeen = /* @__PURE__ */ new Set();
|
|
2439
|
+
for (const a of filtered) {
|
|
2440
|
+
for (const card of a.cards) {
|
|
2441
|
+
if ((card.payment?.chain ?? "solana") !== "solana") {
|
|
2442
|
+
continue;
|
|
2443
|
+
}
|
|
2444
|
+
if (!card.payment?.job_price) {
|
|
2445
|
+
continue;
|
|
2446
|
+
}
|
|
2447
|
+
needsAtaSeen.add(assetFromCardPayment(card.payment).mint !== void 0);
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
const gasByAtaNeed = /* @__PURE__ */ new Map();
|
|
2451
|
+
if (needsAtaSeen.size > 0) {
|
|
2452
|
+
try {
|
|
2453
|
+
const rpc = createSolanaRpc(rpcUrlFor(agent.network));
|
|
2454
|
+
await Promise.all(
|
|
2455
|
+
Array.from(needsAtaSeen).map(async (needsAta) => {
|
|
2456
|
+
const baseline = await estimateNetworkBaseline(rpc, { includeAtaRent: needsAta });
|
|
2457
|
+
gasByAtaNeed.set(needsAta, formatSol$1(Number(baseline.totalLamports)));
|
|
2458
|
+
})
|
|
2459
|
+
);
|
|
2460
|
+
} catch {
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
const results = filtered.map((a) => {
|
|
2464
|
+
const contact = contactByPubkey.get(a.pubkey);
|
|
2465
|
+
return {
|
|
2466
|
+
npub: a.npub,
|
|
2467
|
+
name: sanitizeField(a.name || "", 200),
|
|
2468
|
+
cards: a.cards.map((card) => {
|
|
2469
|
+
const asset = assetFromCardPayment(card.payment);
|
|
2470
|
+
const price = card.payment?.job_price;
|
|
2471
|
+
const gasEstimate = price ? gasByAtaNeed.get(asset.mint !== void 0) : void 0;
|
|
2472
|
+
return {
|
|
2473
|
+
name: sanitizeField(card.name || "", 200),
|
|
2474
|
+
description: sanitizeField(card.description || "", 500),
|
|
2475
|
+
capabilities: card.capabilities,
|
|
2476
|
+
job_price_subunits: price,
|
|
2477
|
+
price_display: price ? formatAssetAmount(asset, BigInt(price)) : "free",
|
|
2478
|
+
asset_token: asset.token,
|
|
2479
|
+
asset_symbol: asset.symbol,
|
|
2480
|
+
asset_mint: asset.mint,
|
|
2481
|
+
chain: card.payment?.chain,
|
|
2482
|
+
network: card.payment?.network,
|
|
2483
|
+
network_fee_estimate_sol: gasEstimate
|
|
2484
|
+
};
|
|
2485
|
+
}),
|
|
2486
|
+
supported_kinds: a.supportedKinds,
|
|
2487
|
+
is_contact: contact ? true : void 0,
|
|
2488
|
+
last_worked_at: contact ? contact.lastJobAt ?? contact.addedAt : void 0,
|
|
2489
|
+
last_capability: contact?.lastCapability,
|
|
2490
|
+
contact_note: contact?.note ? sanitizeField(contact.note, 500) : void 0
|
|
2491
|
+
};
|
|
2492
|
+
});
|
|
2443
2493
|
const { text } = sanitizeUntrusted(JSON.stringify(results, null, 2), "structured");
|
|
2444
2494
|
return textResult(text);
|
|
2445
2495
|
}
|
|
@@ -2571,7 +2621,7 @@ var feedbackContactsTools = [
|
|
|
2571
2621
|
}),
|
|
2572
2622
|
defineTool({
|
|
2573
2623
|
name: "add_contact",
|
|
2574
|
-
description: "Add a provider to the active agent's contacts list (.contacts.json). When the provider has prior jobs in the local history, the contact is enriched with
|
|
2624
|
+
description: "Add a provider to the active agent's contacts list (.contacts.json). When the provider has prior jobs in the local history, the contact is enriched with lastJobAt and lastCapability. Idempotent: re-calling with the same npub updates name/note in place without duplicating the entry.",
|
|
2575
2625
|
schema: AddContactSchema,
|
|
2576
2626
|
async handler(ctx, input) {
|
|
2577
2627
|
ctx.toolRateLimiter.check();
|
|
@@ -2599,14 +2649,12 @@ var feedbackContactsTools = [
|
|
|
2599
2649
|
name: cleanName ?? fallbackProviderName,
|
|
2600
2650
|
note: cleanNote,
|
|
2601
2651
|
lastJobAt: last?.completedAt,
|
|
2602
|
-
lastCapability: last?.capability
|
|
2603
|
-
jobCount: history.length
|
|
2652
|
+
lastCapability: last?.capability
|
|
2604
2653
|
});
|
|
2605
2654
|
const lines = [
|
|
2606
2655
|
`Saved contact ${contact.npub}.`,
|
|
2607
2656
|
contact.name ? ` name: ${contact.name}` : null,
|
|
2608
|
-
contact.lastCapability ? ` last capability: ${contact.lastCapability}` : null
|
|
2609
|
-
contact.jobCount > 0 ? ` prior jobs: ${contact.jobCount}` : null
|
|
2657
|
+
contact.lastCapability ? ` last capability: ${contact.lastCapability}` : null
|
|
2610
2658
|
].filter((line) => line !== null);
|
|
2611
2659
|
return textResult(lines.join("\n"));
|
|
2612
2660
|
}
|
|
@@ -2656,8 +2704,7 @@ var feedbackContactsTools = [
|
|
|
2656
2704
|
note: contact.note !== void 0 ? sanitizeField(contact.note, 500) : void 0,
|
|
2657
2705
|
added_at: contact.addedAt,
|
|
2658
2706
|
last_job_at: contact.lastJobAt,
|
|
2659
|
-
last_capability: contact.lastCapability !== void 0 ? sanitizeField(contact.lastCapability, 200) : void 0
|
|
2660
|
-
job_count: contact.jobCount
|
|
2707
|
+
last_capability: contact.lastCapability !== void 0 ? sanitizeField(contact.lastCapability, 200) : void 0
|
|
2661
2708
|
}));
|
|
2662
2709
|
const { text: wrapped } = sanitizeUntrusted(JSON.stringify(limited, null, 2), "structured");
|
|
2663
2710
|
return textResult(`${limited.length} contact(s):
|