@elisym/sdk 0.10.4 → 0.12.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/README.md +32 -0
- package/dist/agent-store.cjs.map +1 -1
- package/dist/agent-store.js.map +1 -1
- package/dist/index.cjs +356 -106
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +109 -8
- package/dist/index.d.ts +109 -8
- package/dist/index.js +355 -107
- package/dist/index.js.map +1 -1
- package/dist/skills.cjs.map +1 -1
- package/dist/skills.js.map +1 -1
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getAddMemoInstruction } from '@solana-program/memo';
|
|
1
2
|
import { getTransferSolInstruction } from '@solana-program/system';
|
|
2
3
|
import { findAssociatedTokenPda, TOKEN_PROGRAM_ADDRESS, getCreateAssociatedTokenIdempotentInstruction, ASSOCIATED_TOKEN_PROGRAM_ADDRESS, getTransferCheckedInstruction } from '@solana-program/token';
|
|
3
4
|
import { pipe, createTransactionMessage, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, setTransactionMessageComputeUnitLimit, setTransactionMessageComputeUnitPrice, appendTransactionMessageInstructions, signTransactionMessageWithSigners, address, AccountRole, isAddress, getProgramDerivedAddress, assertAccountExists, getAddressDecoder, fetchEncodedAccount, decodeAccount, getStructDecoder, fixDecoderSize, getBytesDecoder, getU8Decoder, getOptionDecoder, getU16Decoder, getBooleanDecoder, getI64Decoder } from '@solana/kit';
|
|
@@ -39,6 +40,7 @@ var LAMPORTS_PER_SOL = 1e9;
|
|
|
39
40
|
var PROTOCOL_FEE_BPS = 300;
|
|
40
41
|
var PROTOCOL_TREASURY = "GY7vnWMkKpftU4nQ16C2ATkj1JwrQpHhknkaBUn67VTy";
|
|
41
42
|
var PROTOCOL_PROGRAM_ID_DEVNET = "BrX1CRkSgvcjxBvc2bgc3QqgWjinusofDmeP7ZVxvwrE";
|
|
43
|
+
var ELISYM_PROTOCOL_TAG = "ELiZksgwDt41LaeuPDLkUfWgFXhGgVayTMP7L5nTSEL8";
|
|
42
44
|
function getProtocolProgramId(cluster) {
|
|
43
45
|
switch (cluster) {
|
|
44
46
|
case "devnet":
|
|
@@ -94,13 +96,13 @@ function decodeConfig(encodedAccount) {
|
|
|
94
96
|
getConfigDecoder()
|
|
95
97
|
);
|
|
96
98
|
}
|
|
97
|
-
async function fetchConfig(rpc,
|
|
98
|
-
const maybeAccount = await fetchMaybeConfig(rpc,
|
|
99
|
+
async function fetchConfig(rpc, address4, config) {
|
|
100
|
+
const maybeAccount = await fetchMaybeConfig(rpc, address4, config);
|
|
99
101
|
assertAccountExists(maybeAccount);
|
|
100
102
|
return maybeAccount;
|
|
101
103
|
}
|
|
102
|
-
async function fetchMaybeConfig(rpc,
|
|
103
|
-
const maybeAccount = await fetchEncodedAccount(rpc,
|
|
104
|
+
async function fetchMaybeConfig(rpc, address4, config) {
|
|
105
|
+
const maybeAccount = await fetchEncodedAccount(rpc, address4, config);
|
|
104
106
|
return decodeConfig(maybeAccount);
|
|
105
107
|
}
|
|
106
108
|
if (process.env.NODE_ENV !== "production") ;
|
|
@@ -572,7 +574,9 @@ var SolanaPaymentStrategy = class {
|
|
|
572
574
|
if (!Number.isInteger(computeUnitLimit) || computeUnitLimit <= 0) {
|
|
573
575
|
throw new Error(`Invalid computeUnitLimit: ${computeUnitLimit}. Must be a positive integer.`);
|
|
574
576
|
}
|
|
575
|
-
const paymentInstructions = await buildPaymentInstructions(paymentRequest, payerSigner
|
|
577
|
+
const paymentInstructions = await buildPaymentInstructions(paymentRequest, payerSigner, {
|
|
578
|
+
jobEventId: options?.jobEventId
|
|
579
|
+
});
|
|
576
580
|
const priorityFeeMicroLamports = options?.priorityFeeMicroLamports ?? await estimatePriorityFeeMicroLamports(rpc, {
|
|
577
581
|
percentile: options?.priorityFeePercentile ?? DEFAULT_PRIORITY_FEE_PERCENTILE
|
|
578
582
|
});
|
|
@@ -869,9 +873,10 @@ function bigIntDelta(post, pre) {
|
|
|
869
873
|
function waitMs(ms) {
|
|
870
874
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
871
875
|
}
|
|
872
|
-
async function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
876
|
+
async function buildPaymentInstructions(paymentRequest, payerSigner, options) {
|
|
873
877
|
const recipient = address(paymentRequest.recipient);
|
|
874
878
|
const reference = address(paymentRequest.reference);
|
|
879
|
+
const protocolTag = address(ELISYM_PROTOCOL_TAG);
|
|
875
880
|
const feeAmount = paymentRequest.fee_amount ?? 0;
|
|
876
881
|
const providerAmount = paymentRequest.fee_address && feeAmount > 0 ? paymentRequest.amount - feeAmount : paymentRequest.amount;
|
|
877
882
|
if (providerAmount <= 0) {
|
|
@@ -879,6 +884,7 @@ async function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
|
879
884
|
`Fee amount (${feeAmount}) exceeds or equals total amount (${paymentRequest.amount}). Cannot create transaction with non-positive provider amount.`
|
|
880
885
|
);
|
|
881
886
|
}
|
|
887
|
+
const memoInstruction = options?.jobEventId ? getAddMemoInstruction({ memo: `elisym:v1:${options.jobEventId}` }) : null;
|
|
882
888
|
const asset = resolveAssetFromPaymentRequest(paymentRequest);
|
|
883
889
|
if (!asset.mint) {
|
|
884
890
|
const providerTransferIx2 = getTransferSolInstruction({
|
|
@@ -886,14 +892,19 @@ async function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
|
886
892
|
destination: recipient,
|
|
887
893
|
amount: BigInt(providerAmount)
|
|
888
894
|
});
|
|
889
|
-
const
|
|
895
|
+
const providerTransferIxWithMarkers2 = {
|
|
890
896
|
...providerTransferIx2,
|
|
891
897
|
accounts: [
|
|
892
898
|
...providerTransferIx2.accounts,
|
|
893
|
-
{ address: reference, role: AccountRole.READONLY }
|
|
899
|
+
{ address: reference, role: AccountRole.READONLY },
|
|
900
|
+
{ address: protocolTag, role: AccountRole.READONLY }
|
|
894
901
|
]
|
|
895
902
|
};
|
|
896
|
-
const instructions2 = [
|
|
903
|
+
const instructions2 = [];
|
|
904
|
+
if (memoInstruction) {
|
|
905
|
+
instructions2.push(memoInstruction);
|
|
906
|
+
}
|
|
907
|
+
instructions2.push(providerTransferIxWithMarkers2);
|
|
897
908
|
if (paymentRequest.fee_address && feeAmount > 0) {
|
|
898
909
|
instructions2.push(
|
|
899
910
|
getTransferSolInstruction({
|
|
@@ -918,6 +929,9 @@ async function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
|
918
929
|
mint
|
|
919
930
|
});
|
|
920
931
|
const instructions = [];
|
|
932
|
+
if (memoInstruction) {
|
|
933
|
+
instructions.push(memoInstruction);
|
|
934
|
+
}
|
|
921
935
|
instructions.push(
|
|
922
936
|
getCreateAssociatedTokenIdempotentInstruction(
|
|
923
937
|
{
|
|
@@ -957,11 +971,15 @@ async function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
|
957
971
|
amount: BigInt(providerAmount),
|
|
958
972
|
decimals: asset.decimals
|
|
959
973
|
});
|
|
960
|
-
const
|
|
974
|
+
const providerTransferIxWithMarkers = {
|
|
961
975
|
...providerTransferIx,
|
|
962
|
-
accounts: [
|
|
976
|
+
accounts: [
|
|
977
|
+
...providerTransferIx.accounts,
|
|
978
|
+
{ address: reference, role: AccountRole.READONLY },
|
|
979
|
+
{ address: protocolTag, role: AccountRole.READONLY }
|
|
980
|
+
]
|
|
963
981
|
};
|
|
964
|
-
instructions.push(
|
|
982
|
+
instructions.push(providerTransferIxWithMarkers);
|
|
965
983
|
if (treasuryAta && paymentRequest.fee_address && feeAmount > 0) {
|
|
966
984
|
instructions.push(
|
|
967
985
|
getTransferCheckedInstruction({
|
|
@@ -989,6 +1007,7 @@ async function createPaymentRequestWithOnchainConfig(rpc, programId, recipient,
|
|
|
989
1007
|
var RANKING_ACTIVITY_WINDOW_SECS = 30 * 24 * 60 * 60;
|
|
990
1008
|
var RANKING_BUCKET_SIZE_SECS = 60;
|
|
991
1009
|
var COLD_START_BUCKET = -Infinity;
|
|
1010
|
+
var NEVER_ABORTED_SIGNAL = new AbortController().signal;
|
|
992
1011
|
function toDTag(name) {
|
|
993
1012
|
const tag = name.toLowerCase().replace(/[^a-z0-9\s-]/g, (ch) => "_" + ch.charCodeAt(0).toString(16).padStart(2, "0")).replace(/\s+/g, "-").replace(/^-+|-+$/g, "");
|
|
994
1013
|
if (!tag) {
|
|
@@ -1018,13 +1037,63 @@ function compareAgentsByRank(a, b) {
|
|
|
1018
1037
|
}
|
|
1019
1038
|
return kb.lastSeen - ka.lastSeen;
|
|
1020
1039
|
}
|
|
1040
|
+
function parseCapabilityEvent(event, network) {
|
|
1041
|
+
if (!verifyEvent(event)) {
|
|
1042
|
+
return null;
|
|
1043
|
+
}
|
|
1044
|
+
if (!event.content) {
|
|
1045
|
+
return null;
|
|
1046
|
+
}
|
|
1047
|
+
let raw;
|
|
1048
|
+
try {
|
|
1049
|
+
raw = JSON.parse(event.content);
|
|
1050
|
+
} catch {
|
|
1051
|
+
return null;
|
|
1052
|
+
}
|
|
1053
|
+
if (!raw || typeof raw !== "object") {
|
|
1054
|
+
return null;
|
|
1055
|
+
}
|
|
1056
|
+
const candidate = raw;
|
|
1057
|
+
if (typeof candidate.name !== "string" || !candidate.name) {
|
|
1058
|
+
return null;
|
|
1059
|
+
}
|
|
1060
|
+
if (typeof candidate.description !== "string") {
|
|
1061
|
+
return null;
|
|
1062
|
+
}
|
|
1063
|
+
if (!Array.isArray(candidate.capabilities) || !candidate.capabilities.every((cap) => typeof cap === "string")) {
|
|
1064
|
+
return null;
|
|
1065
|
+
}
|
|
1066
|
+
if (candidate.deleted) {
|
|
1067
|
+
return null;
|
|
1068
|
+
}
|
|
1069
|
+
const card = candidate;
|
|
1070
|
+
if (card.payment && (typeof card.payment.chain !== "string" || typeof card.payment.network !== "string" || typeof card.payment.address !== "string")) {
|
|
1071
|
+
return null;
|
|
1072
|
+
}
|
|
1073
|
+
if (card.payment?.job_price !== null && card.payment?.job_price !== void 0 && (!Number.isInteger(card.payment.job_price) || card.payment.job_price < 0)) {
|
|
1074
|
+
return null;
|
|
1075
|
+
}
|
|
1076
|
+
const agentNetwork = card.payment?.network ?? "devnet";
|
|
1077
|
+
if (agentNetwork !== network) {
|
|
1078
|
+
return null;
|
|
1079
|
+
}
|
|
1080
|
+
const kTags = event.tags.filter((tag) => tag[0] === "k").map((tag) => parseInt(tag[1] ?? "", 10)).filter((kind) => !isNaN(kind));
|
|
1081
|
+
return {
|
|
1082
|
+
pubkey: event.pubkey,
|
|
1083
|
+
npub: nip19.npubEncode(event.pubkey),
|
|
1084
|
+
cards: [card],
|
|
1085
|
+
eventId: event.id,
|
|
1086
|
+
supportedKinds: kTags,
|
|
1087
|
+
lastSeen: event.created_at
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1021
1090
|
function buildAgentsFromEvents(events, network) {
|
|
1022
1091
|
const latestByDTag = /* @__PURE__ */ new Map();
|
|
1023
1092
|
for (const event of events) {
|
|
1024
1093
|
if (!verifyEvent(event)) {
|
|
1025
1094
|
continue;
|
|
1026
1095
|
}
|
|
1027
|
-
const dTag = event.tags.find((
|
|
1096
|
+
const dTag = event.tags.find((tag) => tag[0] === "d")?.[1] ?? "";
|
|
1028
1097
|
const key = `${event.pubkey}:${dTag}`;
|
|
1029
1098
|
const prev = latestByDTag.get(key);
|
|
1030
1099
|
if (!prev || event.created_at > prev.created_at) {
|
|
@@ -1033,82 +1102,51 @@ function buildAgentsFromEvents(events, network) {
|
|
|
1033
1102
|
}
|
|
1034
1103
|
const accumMap = /* @__PURE__ */ new Map();
|
|
1035
1104
|
for (const event of latestByDTag.values()) {
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
if (raw.deleted) {
|
|
1054
|
-
continue;
|
|
1055
|
-
}
|
|
1056
|
-
const card = raw;
|
|
1057
|
-
if (card.payment && (typeof card.payment.chain !== "string" || typeof card.payment.network !== "string" || typeof card.payment.address !== "string")) {
|
|
1058
|
-
continue;
|
|
1059
|
-
}
|
|
1060
|
-
if (card.payment?.job_price !== null && card.payment?.job_price !== void 0 && (!Number.isInteger(card.payment.job_price) || card.payment.job_price < 0)) {
|
|
1061
|
-
continue;
|
|
1062
|
-
}
|
|
1063
|
-
const agentNetwork = card.payment?.network ?? "devnet";
|
|
1064
|
-
if (agentNetwork !== network) {
|
|
1065
|
-
continue;
|
|
1066
|
-
}
|
|
1067
|
-
const kTags = event.tags.filter((t) => t[0] === "k").map((t) => parseInt(t[1] ?? "", 10)).filter((k) => !isNaN(k));
|
|
1068
|
-
const entry = { card, kTags, createdAt: event.created_at };
|
|
1069
|
-
const existing = accumMap.get(event.pubkey);
|
|
1070
|
-
if (existing) {
|
|
1071
|
-
const dupIndex = existing.entries.findIndex((e) => e.card.name === card.name);
|
|
1072
|
-
if (dupIndex >= 0) {
|
|
1073
|
-
if (entry.createdAt >= existing.entries[dupIndex].createdAt) {
|
|
1074
|
-
existing.entries[dupIndex] = entry;
|
|
1105
|
+
const parsed = parseCapabilityEvent(event, network);
|
|
1106
|
+
if (!parsed) {
|
|
1107
|
+
continue;
|
|
1108
|
+
}
|
|
1109
|
+
const card = parsed.cards[0];
|
|
1110
|
+
const cardKinds = parsed.supportedKinds;
|
|
1111
|
+
const createdAt = parsed.lastSeen;
|
|
1112
|
+
const existing = accumMap.get(parsed.pubkey);
|
|
1113
|
+
if (existing) {
|
|
1114
|
+
const prevForName = existing.perCard.get(card.name);
|
|
1115
|
+
if (prevForName) {
|
|
1116
|
+
if (createdAt >= prevForName.createdAt) {
|
|
1117
|
+
const idx = existing.agent.cards.findIndex(
|
|
1118
|
+
(existingCard) => existingCard.name === card.name
|
|
1119
|
+
);
|
|
1120
|
+
if (idx >= 0) {
|
|
1121
|
+
existing.agent.cards[idx] = card;
|
|
1075
1122
|
}
|
|
1076
|
-
|
|
1077
|
-
existing.entries.push(entry);
|
|
1078
|
-
}
|
|
1079
|
-
if (event.created_at > existing.lastSeen) {
|
|
1080
|
-
existing.lastSeen = event.created_at;
|
|
1081
|
-
existing.eventId = event.id;
|
|
1123
|
+
existing.perCard.set(card.name, { createdAt, kTags: cardKinds });
|
|
1082
1124
|
}
|
|
1083
1125
|
} else {
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
npub: nip19.npubEncode(event.pubkey),
|
|
1087
|
-
entries: [entry],
|
|
1088
|
-
eventId: event.id,
|
|
1089
|
-
lastSeen: event.created_at
|
|
1090
|
-
});
|
|
1126
|
+
existing.agent.cards.push(card);
|
|
1127
|
+
existing.perCard.set(card.name, { createdAt, kTags: cardKinds });
|
|
1091
1128
|
}
|
|
1092
|
-
|
|
1129
|
+
if (createdAt > existing.agent.lastSeen) {
|
|
1130
|
+
existing.agent.lastSeen = createdAt;
|
|
1131
|
+
existing.agent.eventId = parsed.eventId;
|
|
1132
|
+
}
|
|
1133
|
+
} else {
|
|
1134
|
+
accumMap.set(parsed.pubkey, {
|
|
1135
|
+
agent: parsed,
|
|
1136
|
+
perCard: /* @__PURE__ */ new Map([[card.name, { createdAt, kTags: cardKinds }]])
|
|
1137
|
+
});
|
|
1093
1138
|
}
|
|
1094
1139
|
}
|
|
1095
1140
|
const agentMap = /* @__PURE__ */ new Map();
|
|
1096
1141
|
for (const [pubkey, acc] of accumMap) {
|
|
1097
1142
|
const kindsSet = /* @__PURE__ */ new Set();
|
|
1098
|
-
for (const
|
|
1099
|
-
for (const
|
|
1100
|
-
kindsSet.add(
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
|
-
agentMap.set(pubkey,
|
|
1105
|
-
pubkey: acc.pubkey,
|
|
1106
|
-
npub: acc.npub,
|
|
1107
|
-
cards: acc.entries.map((e) => e.card),
|
|
1108
|
-
eventId: acc.eventId,
|
|
1109
|
-
supportedKinds,
|
|
1110
|
-
lastSeen: acc.lastSeen
|
|
1111
|
-
});
|
|
1143
|
+
for (const { kTags } of acc.perCard.values()) {
|
|
1144
|
+
for (const kind of kTags) {
|
|
1145
|
+
kindsSet.add(kind);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
acc.agent.supportedKinds = [...kindsSet];
|
|
1149
|
+
agentMap.set(pubkey, acc.agent);
|
|
1112
1150
|
}
|
|
1113
1151
|
return agentMap;
|
|
1114
1152
|
}
|
|
@@ -1116,21 +1154,6 @@ var DiscoveryService = class {
|
|
|
1116
1154
|
constructor(pool) {
|
|
1117
1155
|
this.pool = pool;
|
|
1118
1156
|
}
|
|
1119
|
-
/** Count elisym agents (kind:31990 with "elisym" tag). */
|
|
1120
|
-
async fetchAllAgentCount() {
|
|
1121
|
-
const events = await this.pool.querySync({
|
|
1122
|
-
kinds: [KIND_APP_HANDLER],
|
|
1123
|
-
"#t": ["elisym"]
|
|
1124
|
-
});
|
|
1125
|
-
const uniquePubkeys = /* @__PURE__ */ new Set();
|
|
1126
|
-
for (const event of events) {
|
|
1127
|
-
if (!verifyEvent(event)) {
|
|
1128
|
-
continue;
|
|
1129
|
-
}
|
|
1130
|
-
uniquePubkeys.add(event.pubkey);
|
|
1131
|
-
}
|
|
1132
|
-
return uniquePubkeys.size;
|
|
1133
|
-
}
|
|
1134
1157
|
/**
|
|
1135
1158
|
* Fetch a single page of elisym agents with relay-side pagination.
|
|
1136
1159
|
* Uses `until` cursor for Nostr cursor-based pagination.
|
|
@@ -1238,6 +1261,19 @@ var DiscoveryService = class {
|
|
|
1238
1261
|
const events = await this.pool.querySync(filter);
|
|
1239
1262
|
const agentMap = buildAgentsFromEvents(events, network);
|
|
1240
1263
|
const agents = Array.from(agentMap.values());
|
|
1264
|
+
return this.runEnrichment(agents, agentMap, NEVER_ABORTED_SIGNAL);
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* Enrich an agent map with paid-job stats, feedback counters, and kind:0
|
|
1268
|
+
* metadata, then return them sorted by `compareAgentsByRank`. Mutates the
|
|
1269
|
+
* passed-in `Agent` objects in place.
|
|
1270
|
+
*
|
|
1271
|
+
* Shared between `fetchAgents` (one-shot) and `streamAgents` (post-EOSE
|
|
1272
|
+
* second pass). The `signal` short-circuits the post-query work; in-flight
|
|
1273
|
+
* pool queries are not cancellable today (they fall through to the standard
|
|
1274
|
+
* timeout) and the caller drops the resolved value.
|
|
1275
|
+
*/
|
|
1276
|
+
async runEnrichment(agents, agentMap, signal) {
|
|
1241
1277
|
const agentPubkeys = Array.from(agentMap.keys());
|
|
1242
1278
|
if (agentPubkeys.length === 0) {
|
|
1243
1279
|
return agents;
|
|
@@ -1245,9 +1281,9 @@ var DiscoveryService = class {
|
|
|
1245
1281
|
const activitySince = Math.floor(Date.now() / 1e3) - RANKING_ACTIVITY_WINDOW_SECS;
|
|
1246
1282
|
const resultKinds = /* @__PURE__ */ new Set();
|
|
1247
1283
|
for (const agent of agentMap.values()) {
|
|
1248
|
-
for (const
|
|
1249
|
-
if (
|
|
1250
|
-
resultKinds.add(KIND_JOB_RESULT_BASE + (
|
|
1284
|
+
for (const supportedKind of agent.supportedKinds) {
|
|
1285
|
+
if (supportedKind >= KIND_JOB_REQUEST_BASE && supportedKind < KIND_JOB_RESULT_BASE) {
|
|
1286
|
+
resultKinds.add(KIND_JOB_RESULT_BASE + (supportedKind - KIND_JOB_REQUEST_BASE));
|
|
1251
1287
|
}
|
|
1252
1288
|
}
|
|
1253
1289
|
}
|
|
@@ -1267,6 +1303,9 @@ var DiscoveryService = class {
|
|
|
1267
1303
|
),
|
|
1268
1304
|
this.enrichWithMetadata(agents)
|
|
1269
1305
|
]);
|
|
1306
|
+
if (signal.aborted) {
|
|
1307
|
+
return agents;
|
|
1308
|
+
}
|
|
1270
1309
|
const deliveredJobsByProvider = /* @__PURE__ */ new Map();
|
|
1271
1310
|
for (const ev of resultEvents) {
|
|
1272
1311
|
if (!verifyEvent(ev)) {
|
|
@@ -1279,7 +1318,7 @@ var DiscoveryService = class {
|
|
|
1279
1318
|
if (ev.created_at > agent.lastSeen) {
|
|
1280
1319
|
agent.lastSeen = ev.created_at;
|
|
1281
1320
|
}
|
|
1282
|
-
const jobEventId = ev.tags.find((
|
|
1321
|
+
const jobEventId = ev.tags.find((tag) => tag[0] === "e")?.[1];
|
|
1283
1322
|
if (jobEventId) {
|
|
1284
1323
|
let delivered = deliveredJobsByProvider.get(ev.pubkey);
|
|
1285
1324
|
if (!delivered) {
|
|
@@ -1293,7 +1332,7 @@ var DiscoveryService = class {
|
|
|
1293
1332
|
if (!verifyEvent(ev)) {
|
|
1294
1333
|
continue;
|
|
1295
1334
|
}
|
|
1296
|
-
const targetPubkey = ev.tags.find((
|
|
1335
|
+
const targetPubkey = ev.tags.find((tag) => tag[0] === "p")?.[1];
|
|
1297
1336
|
if (!targetPubkey) {
|
|
1298
1337
|
continue;
|
|
1299
1338
|
}
|
|
@@ -1304,17 +1343,17 @@ var DiscoveryService = class {
|
|
|
1304
1343
|
if (ev.created_at > agent.lastSeen) {
|
|
1305
1344
|
agent.lastSeen = ev.created_at;
|
|
1306
1345
|
}
|
|
1307
|
-
const rating = ev.tags.find((
|
|
1346
|
+
const rating = ev.tags.find((tag) => tag[0] === "rating")?.[1];
|
|
1308
1347
|
if (rating === "1" || rating === "0") {
|
|
1309
1348
|
agent.totalRatingCount = (agent.totalRatingCount ?? 0) + 1;
|
|
1310
1349
|
if (rating === "1") {
|
|
1311
1350
|
agent.positiveCount = (agent.positiveCount ?? 0) + 1;
|
|
1312
1351
|
}
|
|
1313
1352
|
}
|
|
1314
|
-
const status = ev.tags.find((
|
|
1315
|
-
const txTag = ev.tags.find((
|
|
1353
|
+
const status = ev.tags.find((tag) => tag[0] === "status")?.[1];
|
|
1354
|
+
const txTag = ev.tags.find((tag) => tag[0] === "tx");
|
|
1316
1355
|
const txSignature = txTag?.[1];
|
|
1317
|
-
const jobEventId = ev.tags.find((
|
|
1356
|
+
const jobEventId = ev.tags.find((tag) => tag[0] === "e")?.[1];
|
|
1318
1357
|
const hasDeliveredResult = jobEventId !== void 0 && deliveredJobsByProvider.get(targetPubkey)?.has(jobEventId) === true;
|
|
1319
1358
|
if (status === "payment-completed" && typeof txSignature === "string" && txSignature && hasDeliveredResult) {
|
|
1320
1359
|
if (!agent.lastPaidJobAt || ev.created_at > agent.lastPaidJobAt) {
|
|
@@ -1326,6 +1365,135 @@ var DiscoveryService = class {
|
|
|
1326
1365
|
agents.sort(compareAgentsByRank);
|
|
1327
1366
|
return agents;
|
|
1328
1367
|
}
|
|
1368
|
+
/**
|
|
1369
|
+
* Stream elisym agents progressively as relays deliver events.
|
|
1370
|
+
*
|
|
1371
|
+
* Two live subscriptions:
|
|
1372
|
+
* - kind:31990 (capability cards) - emits `onAgent(agent)` for every new or
|
|
1373
|
+
* updated `(pubkey, d-tag)`. The emitted Agent is the merged view across
|
|
1374
|
+
* all surviving cards for that author.
|
|
1375
|
+
* - kind:6100 (default-offset job results) tagged `t=elisym` since 30d ago -
|
|
1376
|
+
* emits `onPaidJob(pubkey, ts)` for each delivered result. Custom-kind
|
|
1377
|
+
* results (offset != 100) are not on this stream; they enter the final
|
|
1378
|
+
* ranking via the post-EOSE enrichment pass.
|
|
1379
|
+
*
|
|
1380
|
+
* After capabilities EOSE, an enrichment pass runs in parallel to the live
|
|
1381
|
+
* subscriptions and produces a ranked snapshot via `onComplete`. The snapshot
|
|
1382
|
+
* is a clone, so further live updates do not mutate it.
|
|
1383
|
+
*
|
|
1384
|
+
* `closer.close()` tears down both subscriptions and aborts an in-flight
|
|
1385
|
+
* enrichment. If `opts.signal` is provided, aborting it does the same.
|
|
1386
|
+
*/
|
|
1387
|
+
streamAgents(network, opts) {
|
|
1388
|
+
const eventsByPubkey = /* @__PURE__ */ new Map();
|
|
1389
|
+
const agentByPubkey = /* @__PURE__ */ new Map();
|
|
1390
|
+
const eoseSeen = { caps: false, results: false };
|
|
1391
|
+
let enrichmentStarted = false;
|
|
1392
|
+
const enrichmentAbort = new AbortController();
|
|
1393
|
+
const onExternalAbort = () => enrichmentAbort.abort();
|
|
1394
|
+
if (opts.signal) {
|
|
1395
|
+
if (opts.signal.aborted) {
|
|
1396
|
+
enrichmentAbort.abort();
|
|
1397
|
+
} else {
|
|
1398
|
+
opts.signal.addEventListener("abort", onExternalAbort, { once: true });
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
const checkEose = () => {
|
|
1402
|
+
if (eoseSeen.caps && eoseSeen.results) {
|
|
1403
|
+
opts.onEose?.();
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1406
|
+
const startEnrichment = () => {
|
|
1407
|
+
if (enrichmentStarted) {
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
enrichmentStarted = true;
|
|
1411
|
+
const snapshotAgents = Array.from(agentByPubkey.values()).map((agent) => ({ ...agent }));
|
|
1412
|
+
const snapshotMap = new Map(snapshotAgents.map((agent) => [agent.pubkey, agent]));
|
|
1413
|
+
void this.runEnrichment(snapshotAgents, snapshotMap, enrichmentAbort.signal).then(
|
|
1414
|
+
(sorted) => {
|
|
1415
|
+
if (enrichmentAbort.signal.aborted) {
|
|
1416
|
+
return;
|
|
1417
|
+
}
|
|
1418
|
+
opts.onComplete?.(sorted);
|
|
1419
|
+
},
|
|
1420
|
+
() => {
|
|
1421
|
+
}
|
|
1422
|
+
);
|
|
1423
|
+
};
|
|
1424
|
+
const capSub = this.pool.subscribe(
|
|
1425
|
+
{ kinds: [KIND_APP_HANDLER], "#t": ["elisym"] },
|
|
1426
|
+
(event) => {
|
|
1427
|
+
const dTag = event.tags.find((tag) => tag[0] === "d")?.[1] ?? "";
|
|
1428
|
+
let perDTag = eventsByPubkey.get(event.pubkey);
|
|
1429
|
+
const prev = perDTag?.get(dTag);
|
|
1430
|
+
if (prev && event.created_at <= prev.created_at) {
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
if (!verifyEvent(event)) {
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1436
|
+
if (!event.content) {
|
|
1437
|
+
return;
|
|
1438
|
+
}
|
|
1439
|
+
let payload;
|
|
1440
|
+
try {
|
|
1441
|
+
payload = JSON.parse(event.content);
|
|
1442
|
+
} catch {
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1445
|
+
const isTombstone = payload !== null && typeof payload === "object" && Boolean(payload.deleted);
|
|
1446
|
+
if (!isTombstone && !parseCapabilityEvent(event, network)) {
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
if (!perDTag) {
|
|
1450
|
+
perDTag = /* @__PURE__ */ new Map();
|
|
1451
|
+
eventsByPubkey.set(event.pubkey, perDTag);
|
|
1452
|
+
}
|
|
1453
|
+
perDTag.set(dTag, event);
|
|
1454
|
+
const merged = buildAgentsFromEvents(Array.from(perDTag.values()), network).get(
|
|
1455
|
+
event.pubkey
|
|
1456
|
+
);
|
|
1457
|
+
if (!merged) {
|
|
1458
|
+
agentByPubkey.delete(event.pubkey);
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
agentByPubkey.set(event.pubkey, merged);
|
|
1462
|
+
opts.onAgent(merged);
|
|
1463
|
+
},
|
|
1464
|
+
{
|
|
1465
|
+
oneose: () => {
|
|
1466
|
+
eoseSeen.caps = true;
|
|
1467
|
+
startEnrichment();
|
|
1468
|
+
checkEose();
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
);
|
|
1472
|
+
const activitySince = Math.floor(Date.now() / 1e3) - RANKING_ACTIVITY_WINDOW_SECS;
|
|
1473
|
+
const resultsSub = this.pool.subscribe(
|
|
1474
|
+
{ kinds: [KIND_JOB_RESULT], "#t": ["elisym"], since: activitySince },
|
|
1475
|
+
(event) => {
|
|
1476
|
+
if (!verifyEvent(event)) {
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
opts.onPaidJob?.(event.pubkey, event.created_at);
|
|
1480
|
+
},
|
|
1481
|
+
{
|
|
1482
|
+
oneose: () => {
|
|
1483
|
+
eoseSeen.results = true;
|
|
1484
|
+
checkEose();
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
);
|
|
1488
|
+
return {
|
|
1489
|
+
close: (reason) => {
|
|
1490
|
+
capSub.close(reason);
|
|
1491
|
+
resultsSub.close(reason);
|
|
1492
|
+
enrichmentAbort.abort();
|
|
1493
|
+
opts.signal?.removeEventListener("abort", onExternalAbort);
|
|
1494
|
+
}
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1329
1497
|
/**
|
|
1330
1498
|
* Publish a capability card (kind:31990) as a provider.
|
|
1331
1499
|
* Solana address is validated for Base58 format only - full decode
|
|
@@ -2541,8 +2709,11 @@ var NostrPool = class {
|
|
|
2541
2709
|
throw new Error(`Failed to publish to all ${this.relays.length} relays`);
|
|
2542
2710
|
}
|
|
2543
2711
|
}
|
|
2544
|
-
subscribe(filter, onEvent) {
|
|
2545
|
-
const rawSub = this.pool.subscribeMany(this.relays, filter, {
|
|
2712
|
+
subscribe(filter, onEvent, opts) {
|
|
2713
|
+
const rawSub = this.pool.subscribeMany(this.relays, filter, {
|
|
2714
|
+
onevent: onEvent,
|
|
2715
|
+
oneose: opts?.oneose
|
|
2716
|
+
});
|
|
2546
2717
|
const tracked = {
|
|
2547
2718
|
close: (reason) => {
|
|
2548
2719
|
this.activeSubscriptions.delete(tracked);
|
|
@@ -2893,6 +3064,83 @@ async function doVerifyOnce(rpc, txSignature, expectedRecipient) {
|
|
|
2893
3064
|
}
|
|
2894
3065
|
return { verified: false, txSignature: sigStr, reason: "recipient_mismatch" };
|
|
2895
3066
|
}
|
|
3067
|
+
var DEFAULT_LIMIT = 1e3;
|
|
3068
|
+
var NATIVE_KEY = "native";
|
|
3069
|
+
async function aggregateNetworkStats(rpc, options) {
|
|
3070
|
+
const limit = options?.limit ?? DEFAULT_LIMIT;
|
|
3071
|
+
const concurrency = options?.concurrency ?? DEFAULTS.QUERY_MAX_CONCURRENCY;
|
|
3072
|
+
const tag = address(ELISYM_PROTOCOL_TAG);
|
|
3073
|
+
const signatures = await rpc.getSignaturesForAddress(tag, { limit, before: options?.before }).send();
|
|
3074
|
+
const validSigs = signatures.filter((entry) => entry.err === null);
|
|
3075
|
+
if (validSigs.length === 0) {
|
|
3076
|
+
return { jobCount: 0, volumeByAsset: {} };
|
|
3077
|
+
}
|
|
3078
|
+
const volumeByAsset = {};
|
|
3079
|
+
let jobCount = 0;
|
|
3080
|
+
for (let start = 0; start < validSigs.length; start += concurrency) {
|
|
3081
|
+
const batch = validSigs.slice(start, start + concurrency);
|
|
3082
|
+
const txResults = await Promise.all(
|
|
3083
|
+
batch.map(
|
|
3084
|
+
(entry) => rpc.getTransaction(entry.signature, {
|
|
3085
|
+
commitment: "confirmed",
|
|
3086
|
+
encoding: "json",
|
|
3087
|
+
maxSupportedTransactionVersion: 0
|
|
3088
|
+
}).send().catch(() => null)
|
|
3089
|
+
)
|
|
3090
|
+
);
|
|
3091
|
+
for (const tx of txResults) {
|
|
3092
|
+
if (!tx?.meta || tx.meta.err) {
|
|
3093
|
+
continue;
|
|
3094
|
+
}
|
|
3095
|
+
jobCount += 1;
|
|
3096
|
+
accumulateTransfers(tx, volumeByAsset);
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
const latest = validSigs[0]?.signature;
|
|
3100
|
+
const oldest = validSigs.at(-1)?.signature;
|
|
3101
|
+
return {
|
|
3102
|
+
jobCount,
|
|
3103
|
+
volumeByAsset,
|
|
3104
|
+
latestSignature: latest,
|
|
3105
|
+
oldestSignature: oldest
|
|
3106
|
+
};
|
|
3107
|
+
}
|
|
3108
|
+
function accumulateTransfers(tx, volumeByAsset) {
|
|
3109
|
+
const raw = tx;
|
|
3110
|
+
const meta = raw.meta;
|
|
3111
|
+
if (!meta) {
|
|
3112
|
+
return;
|
|
3113
|
+
}
|
|
3114
|
+
const preTokens = meta.preTokenBalances ?? [];
|
|
3115
|
+
const postTokens = meta.postTokenBalances ?? [];
|
|
3116
|
+
const isSpl = postTokens.length > 0 || preTokens.length > 0;
|
|
3117
|
+
if (isSpl) {
|
|
3118
|
+
accumulateSplDeltas(preTokens, postTokens, volumeByAsset);
|
|
3119
|
+
return;
|
|
3120
|
+
}
|
|
3121
|
+
accumulateNativeDeltas(meta.preBalances, meta.postBalances, volumeByAsset);
|
|
3122
|
+
}
|
|
3123
|
+
function accumulateSplDeltas(pre, post, volumeByAsset) {
|
|
3124
|
+
for (const postEntry of post) {
|
|
3125
|
+
const preEntry = pre.find((entry) => entry.accountIndex === postEntry.accountIndex);
|
|
3126
|
+
const preAmount = preEntry ? BigInt(preEntry.uiTokenAmount.amount) : 0n;
|
|
3127
|
+
const postAmount = BigInt(postEntry.uiTokenAmount.amount);
|
|
3128
|
+
const delta = postAmount - preAmount;
|
|
3129
|
+
if (delta > 0n) {
|
|
3130
|
+
volumeByAsset[postEntry.mint] = (volumeByAsset[postEntry.mint] ?? 0n) + delta;
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
function accumulateNativeDeltas(pre, post, volumeByAsset) {
|
|
3135
|
+
for (let i = 1; i < post.length; i++) {
|
|
3136
|
+
const preValue = pre[i] ?? 0n;
|
|
3137
|
+
const postValue = post[i] ?? 0n;
|
|
3138
|
+
const delta = BigInt(postValue) - BigInt(preValue);
|
|
3139
|
+
if (delta > 0n) {
|
|
3140
|
+
volumeByAsset[NATIVE_KEY] = (volumeByAsset[NATIVE_KEY] ?? 0n) + delta;
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
2896
3144
|
var SessionSpendLimitEntrySchema = z.object({
|
|
2897
3145
|
chain: z.enum(["solana"]),
|
|
2898
3146
|
token: z.string().min(1).max(16).regex(/^[a-z0-9]+$/, "token must be lowercase alphanumeric"),
|
|
@@ -3093,6 +3341,6 @@ function makeCensor() {
|
|
|
3093
3341
|
};
|
|
3094
3342
|
}
|
|
3095
3343
|
|
|
3096
|
-
export { BoundedSet, DEFAULTS, DEFAULT_KIND_OFFSET, DEFAULT_REDACT_PATHS, DiscoveryService, ElisymClient, ElisymIdentity, GlobalConfigSchema, INPUT_REDACT_PATHS, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, KNOWN_ASSETS, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, NATIVE_SOL, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, PaymentRequestSchema, PingService, RELAYS, SECRET_REDACT_PATHS, SessionSpendLimitEntrySchema, SolanaPaymentStrategy, USDC_SOLANA_DEVNET, assertExpiry, assertLamports, assetByKey, assetKey, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, clearQuickVerifyCache, compareAgentsByRank, computeRankKey, createPaymentRequestWithOnchainConfig, createSlidingWindowLimiter, estimatePriorityFeeMicroLamports, estimateSolFeeLamports, formatAssetAmount, formatFeeBreakdown, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, makeCensor, nip44Decrypt, nip44Encrypt, parseAssetAmount, parsePaymentRequest, pickPercentileFee, resolveAssetFromPaymentRequest, resolveKnownAsset, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry, verifyJobPaymentQuick };
|
|
3344
|
+
export { BoundedSet, DEFAULTS, DEFAULT_KIND_OFFSET, DEFAULT_REDACT_PATHS, DiscoveryService, ELISYM_PROTOCOL_TAG, ElisymClient, ElisymIdentity, GlobalConfigSchema, INPUT_REDACT_PATHS, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, KNOWN_ASSETS, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, NATIVE_SOL, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, PaymentRequestSchema, PingService, RELAYS, SECRET_REDACT_PATHS, SessionSpendLimitEntrySchema, SolanaPaymentStrategy, USDC_SOLANA_DEVNET, aggregateNetworkStats, assertExpiry, assertLamports, assetByKey, assetKey, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, clearQuickVerifyCache, compareAgentsByRank, computeRankKey, createPaymentRequestWithOnchainConfig, createSlidingWindowLimiter, estimatePriorityFeeMicroLamports, estimateSolFeeLamports, formatAssetAmount, formatFeeBreakdown, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, makeCensor, nip44Decrypt, nip44Encrypt, parseAssetAmount, parsePaymentRequest, pickPercentileFee, resolveAssetFromPaymentRequest, resolveKnownAsset, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry, verifyJobPaymentQuick };
|
|
3097
3345
|
//# sourceMappingURL=index.js.map
|
|
3098
3346
|
//# sourceMappingURL=index.js.map
|