@elisym/sdk 0.10.3 → 0.10.4

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.cjs CHANGED
@@ -1236,18 +1236,20 @@ var DiscoveryService = class {
1236
1236
  *
1237
1237
  * Ranking algorithm:
1238
1238
  * 1. Bucket each agent into 1-minute slots by `lastPaidJobAt` (newest
1239
- * `payment-completed` feedback timestamp). Cold-start agents go into a
1240
- * sentinel bucket below all populated buckets.
1239
+ * `payment-completed` feedback timestamp, gated by a matching kind:6xxx
1240
+ * result from the provider on the same job event). Cold-start agents go
1241
+ * into a sentinel bucket below all populated buckets.
1241
1242
  * 2. Within a bucket, sort by positive review rate descending.
1242
1243
  * 3. Tiebreak by raw `lastPaidJobAt`, then `lastSeen` (NIP-89 freshness).
1243
1244
  *
1244
- * NOTE: We trust the `payment-completed` feedback timestamp directly; we do
1245
- * not verify the embedded `tx` signature on-chain. Public Solana devnet RPC
1246
- * rate-limits trivially exceed what discovery needs (N agents * up-to-5
1247
- * candidates), and the resulting 429s blocked discovery entirely. This
1248
- * means a malicious customer can publish a fake `payment-completed` to lift
1249
- * an agent's ranking. Acceptable trade-off for devnet / MVP; tighten via
1250
- * recipient-tied checks when the network moves to mainnet with a paid RPC
1245
+ * NOTE: We do not verify the `tx` signature on-chain - public Solana devnet
1246
+ * RPC rate-limits trivially exceed what discovery needs (N agents * up-to-5
1247
+ * candidates), and the resulting 429s blocked discovery entirely. As a
1248
+ * lighter sybil mitigation we cross-check `payment-completed` feedback
1249
+ * against a kind:6xxx result event authored by the provider on the same
1250
+ * job: a customer can publish a fake `payment-completed`, but they cannot
1251
+ * forge a result event signed by the provider. Tighten with recipient-tied
1252
+ * on-chain checks when the network moves to mainnet with a paid RPC
1251
1253
  * provider.
1252
1254
  */
1253
1255
  async fetchAgents(network = "devnet", limit) {
@@ -1290,14 +1292,27 @@ var DiscoveryService = class {
1290
1292
  ),
1291
1293
  this.enrichWithMetadata(agents)
1292
1294
  ]);
1295
+ const deliveredJobsByProvider = /* @__PURE__ */ new Map();
1293
1296
  for (const ev of resultEvents) {
1294
1297
  if (!nostrTools.verifyEvent(ev)) {
1295
1298
  continue;
1296
1299
  }
1297
1300
  const agent = agentMap.get(ev.pubkey);
1298
- if (agent && ev.created_at > agent.lastSeen) {
1301
+ if (!agent) {
1302
+ continue;
1303
+ }
1304
+ if (ev.created_at > agent.lastSeen) {
1299
1305
  agent.lastSeen = ev.created_at;
1300
1306
  }
1307
+ const jobEventId = ev.tags.find((t) => t[0] === "e")?.[1];
1308
+ if (jobEventId) {
1309
+ let delivered = deliveredJobsByProvider.get(ev.pubkey);
1310
+ if (!delivered) {
1311
+ delivered = /* @__PURE__ */ new Set();
1312
+ deliveredJobsByProvider.set(ev.pubkey, delivered);
1313
+ }
1314
+ delivered.add(jobEventId);
1315
+ }
1301
1316
  }
1302
1317
  for (const ev of feedbackEvents) {
1303
1318
  if (!nostrTools.verifyEvent(ev)) {
@@ -1324,7 +1339,9 @@ var DiscoveryService = class {
1324
1339
  const status = ev.tags.find((t) => t[0] === "status")?.[1];
1325
1340
  const txTag = ev.tags.find((t) => t[0] === "tx");
1326
1341
  const txSignature = txTag?.[1];
1327
- if (status === "payment-completed" && typeof txSignature === "string" && txSignature) {
1342
+ const jobEventId = ev.tags.find((t) => t[0] === "e")?.[1];
1343
+ const hasDeliveredResult = jobEventId !== void 0 && deliveredJobsByProvider.get(targetPubkey)?.has(jobEventId) === true;
1344
+ if (status === "payment-completed" && typeof txSignature === "string" && txSignature && hasDeliveredResult) {
1328
1345
  if (!agent.lastPaidJobAt || ev.created_at > agent.lastPaidJobAt) {
1329
1346
  agent.lastPaidJobAt = ev.created_at;
1330
1347
  agent.lastPaidJobTx = txSignature;