@elisym/mcp 0.8.12 → 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 CHANGED
@@ -2336,7 +2336,7 @@ var GetIdentitySchema = z.object({});
2336
2336
  var discoveryTools = [
2337
2337
  defineTool({
2338
2338
  name: "search_agents",
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.",
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.",
2340
2340
  schema: SearchAgentsSchema,
2341
2341
  async handler(ctx, input) {
2342
2342
  const { capabilities, query, max_price_lamports, include_offline, contacts_only } = input;
@@ -2344,25 +2344,27 @@ var discoveryTools = [
2344
2344
  return errorResult(`Too many capabilities (max ${MAX_CAPABILITIES})`);
2345
2345
  }
2346
2346
  const agent = ctx.active();
2347
- let lastWorkedAtByPubkey = /* @__PURE__ */ new Map();
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
+ }
2348
2354
  if (contacts_only) {
2349
2355
  if (!agent.agentDir) {
2350
2356
  return textResult(
2351
2357
  "contacts_only=true requires a persistent agent (no on-disk directory for the active agent)."
2352
2358
  );
2353
2359
  }
2354
- const data = await readContacts(agent.agentDir);
2355
- if (data.contacts.length === 0) {
2360
+ if (contactByPubkey.size === 0) {
2356
2361
  return textResult(
2357
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."
2358
2363
  );
2359
2364
  }
2360
- lastWorkedAtByPubkey = new Map(
2361
- data.contacts.map((contact) => [contact.pubkey, contact.lastJobAt ?? contact.addedAt])
2362
- );
2363
2365
  }
2364
2366
  const agents = await agent.client.discovery.fetchAgents(agent.network);
2365
- let filtered = contacts_only ? agents.filter((a) => lastWorkedAtByPubkey.has(a.pubkey)) : agents;
2367
+ let filtered = contacts_only ? agents.filter((a) => contactByPubkey.has(a.pubkey)) : agents;
2366
2368
  filtered = filtered.filter(
2367
2369
  (a) => a.cards.some(
2368
2370
  (card) => capabilities.some(
@@ -2405,10 +2407,28 @@ var discoveryTools = [
2405
2407
  hits++;
2406
2408
  }
2407
2409
  }
2408
- return { agent: a, score: hits };
2410
+ const contactBoost = contactByPubkey.has(a.pubkey) ? 0.5 : 0;
2411
+ return { agent: a, score: hits + contactBoost };
2409
2412
  });
2410
2413
  filtered = scored.filter((s) => s.score > 0).sort((a, b) => b.score - a.score).map((s) => s.agent);
2411
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
+ });
2412
2432
  }
2413
2433
  if (filtered.length === 0) {
2414
2434
  return textResult(
@@ -2440,30 +2460,36 @@ var discoveryTools = [
2440
2460
  } catch {
2441
2461
  }
2442
2462
  }
2443
- const results = filtered.map((a) => ({
2444
- npub: a.npub,
2445
- name: sanitizeField(a.name || "", 200),
2446
- cards: a.cards.map((card) => {
2447
- const asset = assetFromCardPayment(card.payment);
2448
- const price = card.payment?.job_price;
2449
- const gasEstimate = price ? gasByAtaNeed.get(asset.mint !== void 0) : void 0;
2450
- return {
2451
- name: sanitizeField(card.name || "", 200),
2452
- description: sanitizeField(card.description || "", 500),
2453
- capabilities: card.capabilities,
2454
- job_price_subunits: price,
2455
- price_display: price ? formatAssetAmount(asset, BigInt(price)) : "free",
2456
- asset_token: asset.token,
2457
- asset_symbol: asset.symbol,
2458
- asset_mint: asset.mint,
2459
- chain: card.payment?.chain,
2460
- network: card.payment?.network,
2461
- network_fee_estimate_sol: gasEstimate
2462
- };
2463
- }),
2464
- supported_kinds: a.supportedKinds,
2465
- last_worked_at: contacts_only ? lastWorkedAtByPubkey.get(a.pubkey) : void 0
2466
- }));
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
+ });
2467
2493
  const { text } = sanitizeUntrusted(JSON.stringify(results, null, 2), "structured");
2468
2494
  return textResult(text);
2469
2495
  }