@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.d.cts CHANGED
@@ -381,18 +381,20 @@ declare class DiscoveryService {
381
381
  *
382
382
  * Ranking algorithm:
383
383
  * 1. Bucket each agent into 1-minute slots by `lastPaidJobAt` (newest
384
- * `payment-completed` feedback timestamp). Cold-start agents go into a
385
- * sentinel bucket below all populated buckets.
384
+ * `payment-completed` feedback timestamp, gated by a matching kind:6xxx
385
+ * result from the provider on the same job event). Cold-start agents go
386
+ * into a sentinel bucket below all populated buckets.
386
387
  * 2. Within a bucket, sort by positive review rate descending.
387
388
  * 3. Tiebreak by raw `lastPaidJobAt`, then `lastSeen` (NIP-89 freshness).
388
389
  *
389
- * NOTE: We trust the `payment-completed` feedback timestamp directly; we do
390
- * not verify the embedded `tx` signature on-chain. Public Solana devnet RPC
391
- * rate-limits trivially exceed what discovery needs (N agents * up-to-5
392
- * candidates), and the resulting 429s blocked discovery entirely. This
393
- * means a malicious customer can publish a fake `payment-completed` to lift
394
- * an agent's ranking. Acceptable trade-off for devnet / MVP; tighten via
395
- * recipient-tied checks when the network moves to mainnet with a paid RPC
390
+ * NOTE: We do not verify the `tx` signature on-chain - public Solana devnet
391
+ * RPC rate-limits trivially exceed what discovery needs (N agents * up-to-5
392
+ * candidates), and the resulting 429s blocked discovery entirely. As a
393
+ * lighter sybil mitigation we cross-check `payment-completed` feedback
394
+ * against a kind:6xxx result event authored by the provider on the same
395
+ * job: a customer can publish a fake `payment-completed`, but they cannot
396
+ * forge a result event signed by the provider. Tighten with recipient-tied
397
+ * on-chain checks when the network moves to mainnet with a paid RPC
396
398
  * provider.
397
399
  */
398
400
  fetchAgents(network?: Network, limit?: number): Promise<Agent[]>;
package/dist/index.d.ts CHANGED
@@ -381,18 +381,20 @@ declare class DiscoveryService {
381
381
  *
382
382
  * Ranking algorithm:
383
383
  * 1. Bucket each agent into 1-minute slots by `lastPaidJobAt` (newest
384
- * `payment-completed` feedback timestamp). Cold-start agents go into a
385
- * sentinel bucket below all populated buckets.
384
+ * `payment-completed` feedback timestamp, gated by a matching kind:6xxx
385
+ * result from the provider on the same job event). Cold-start agents go
386
+ * into a sentinel bucket below all populated buckets.
386
387
  * 2. Within a bucket, sort by positive review rate descending.
387
388
  * 3. Tiebreak by raw `lastPaidJobAt`, then `lastSeen` (NIP-89 freshness).
388
389
  *
389
- * NOTE: We trust the `payment-completed` feedback timestamp directly; we do
390
- * not verify the embedded `tx` signature on-chain. Public Solana devnet RPC
391
- * rate-limits trivially exceed what discovery needs (N agents * up-to-5
392
- * candidates), and the resulting 429s blocked discovery entirely. This
393
- * means a malicious customer can publish a fake `payment-completed` to lift
394
- * an agent's ranking. Acceptable trade-off for devnet / MVP; tighten via
395
- * recipient-tied checks when the network moves to mainnet with a paid RPC
390
+ * NOTE: We do not verify the `tx` signature on-chain - public Solana devnet
391
+ * RPC rate-limits trivially exceed what discovery needs (N agents * up-to-5
392
+ * candidates), and the resulting 429s blocked discovery entirely. As a
393
+ * lighter sybil mitigation we cross-check `payment-completed` feedback
394
+ * against a kind:6xxx result event authored by the provider on the same
395
+ * job: a customer can publish a fake `payment-completed`, but they cannot
396
+ * forge a result event signed by the provider. Tighten with recipient-tied
397
+ * on-chain checks when the network moves to mainnet with a paid RPC
396
398
  * provider.
397
399
  */
398
400
  fetchAgents(network?: Network, limit?: number): Promise<Agent[]>;
package/dist/index.js CHANGED
@@ -1211,18 +1211,20 @@ var DiscoveryService = class {
1211
1211
  *
1212
1212
  * Ranking algorithm:
1213
1213
  * 1. Bucket each agent into 1-minute slots by `lastPaidJobAt` (newest
1214
- * `payment-completed` feedback timestamp). Cold-start agents go into a
1215
- * sentinel bucket below all populated buckets.
1214
+ * `payment-completed` feedback timestamp, gated by a matching kind:6xxx
1215
+ * result from the provider on the same job event). Cold-start agents go
1216
+ * into a sentinel bucket below all populated buckets.
1216
1217
  * 2. Within a bucket, sort by positive review rate descending.
1217
1218
  * 3. Tiebreak by raw `lastPaidJobAt`, then `lastSeen` (NIP-89 freshness).
1218
1219
  *
1219
- * NOTE: We trust the `payment-completed` feedback timestamp directly; we do
1220
- * not verify the embedded `tx` signature on-chain. Public Solana devnet RPC
1221
- * rate-limits trivially exceed what discovery needs (N agents * up-to-5
1222
- * candidates), and the resulting 429s blocked discovery entirely. This
1223
- * means a malicious customer can publish a fake `payment-completed` to lift
1224
- * an agent's ranking. Acceptable trade-off for devnet / MVP; tighten via
1225
- * recipient-tied checks when the network moves to mainnet with a paid RPC
1220
+ * NOTE: We do not verify the `tx` signature on-chain - public Solana devnet
1221
+ * RPC rate-limits trivially exceed what discovery needs (N agents * up-to-5
1222
+ * candidates), and the resulting 429s blocked discovery entirely. As a
1223
+ * lighter sybil mitigation we cross-check `payment-completed` feedback
1224
+ * against a kind:6xxx result event authored by the provider on the same
1225
+ * job: a customer can publish a fake `payment-completed`, but they cannot
1226
+ * forge a result event signed by the provider. Tighten with recipient-tied
1227
+ * on-chain checks when the network moves to mainnet with a paid RPC
1226
1228
  * provider.
1227
1229
  */
1228
1230
  async fetchAgents(network = "devnet", limit) {
@@ -1265,14 +1267,27 @@ var DiscoveryService = class {
1265
1267
  ),
1266
1268
  this.enrichWithMetadata(agents)
1267
1269
  ]);
1270
+ const deliveredJobsByProvider = /* @__PURE__ */ new Map();
1268
1271
  for (const ev of resultEvents) {
1269
1272
  if (!verifyEvent(ev)) {
1270
1273
  continue;
1271
1274
  }
1272
1275
  const agent = agentMap.get(ev.pubkey);
1273
- if (agent && ev.created_at > agent.lastSeen) {
1276
+ if (!agent) {
1277
+ continue;
1278
+ }
1279
+ if (ev.created_at > agent.lastSeen) {
1274
1280
  agent.lastSeen = ev.created_at;
1275
1281
  }
1282
+ const jobEventId = ev.tags.find((t) => t[0] === "e")?.[1];
1283
+ if (jobEventId) {
1284
+ let delivered = deliveredJobsByProvider.get(ev.pubkey);
1285
+ if (!delivered) {
1286
+ delivered = /* @__PURE__ */ new Set();
1287
+ deliveredJobsByProvider.set(ev.pubkey, delivered);
1288
+ }
1289
+ delivered.add(jobEventId);
1290
+ }
1276
1291
  }
1277
1292
  for (const ev of feedbackEvents) {
1278
1293
  if (!verifyEvent(ev)) {
@@ -1299,7 +1314,9 @@ var DiscoveryService = class {
1299
1314
  const status = ev.tags.find((t) => t[0] === "status")?.[1];
1300
1315
  const txTag = ev.tags.find((t) => t[0] === "tx");
1301
1316
  const txSignature = txTag?.[1];
1302
- if (status === "payment-completed" && typeof txSignature === "string" && txSignature) {
1317
+ const jobEventId = ev.tags.find((t) => t[0] === "e")?.[1];
1318
+ const hasDeliveredResult = jobEventId !== void 0 && deliveredJobsByProvider.get(targetPubkey)?.has(jobEventId) === true;
1319
+ if (status === "payment-completed" && typeof txSignature === "string" && txSignature && hasDeliveredResult) {
1303
1320
  if (!agent.lastPaidJobAt || ev.created_at > agent.lastPaidJobAt) {
1304
1321
  agent.lastPaidJobAt = ev.created_at;
1305
1322
  agent.lastPaidJobTx = txSignature;