@cascade-fyi/sati-agent0-sdk 0.1.0 → 0.1.1

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.mjs CHANGED
@@ -615,7 +615,7 @@ var SatiAgent = class SatiAgent {
615
615
  const transferIx = getTransferInstruction({
616
616
  source: sourceAta,
617
617
  destination: destAta,
618
- authority: { address: ownerAddr },
618
+ authority: ownerAddr,
619
619
  amount: 1n
620
620
  });
621
621
  return { signature: await access.sender.signAndSend([createAtaIx, transferIx]) };
@@ -652,30 +652,39 @@ var SatiAgent = class SatiAgent {
652
652
  const agentMint = await generateKeyPairSigner();
653
653
  const rpc = this._sdk.sati.getRpc();
654
654
  const [registryConfigAddress] = await findRegistryConfigPda();
655
- const registryConfig = await fetchRegistryConfig(rpc, registryConfigAddress);
656
- const groupMint = registryConfig.data.groupMint;
657
655
  const ownerAddress = address(access.sender.address);
658
656
  const [agentTokenAccount] = await findAssociatedTokenAddress(agentMint.address, ownerAddress);
659
- const [agentIndex] = await findAgentIndexPda(registryConfig.data.totalAgents + 1n);
660
- const registerIx = await getRegisterAgentInstructionAsync({
661
- payer: { address: ownerAddress },
662
- owner: ownerAddress,
663
- groupMint,
664
- agentMint,
665
- agentTokenAccount,
666
- agentIndex,
667
- name: this._registrationFile.name,
668
- symbol: "",
669
- uri,
670
- additionalMetadata: null,
671
- nonTransferable: false
672
- });
673
- const signature = await access.sender.signAndSend([registerIx], [agentMint]);
674
- const finalMemberNumber = (await fetchRegistryConfig(rpc, registryConfigAddress)).data.totalAgents;
675
- return {
676
- signature,
677
- agentId: this._storeIdentity(agentMint.address, ownerAddress, uri, finalMemberNumber)
678
- };
657
+ const MAX_RETRIES = 3;
658
+ const RETRY_DELAY_MS = 1500;
659
+ let lastError;
660
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) try {
661
+ const registryConfig = await fetchRegistryConfig(rpc, registryConfigAddress);
662
+ const groupMint = registryConfig.data.groupMint;
663
+ const memberNumber = registryConfig.data.totalAgents + 1n;
664
+ const [agentIndex] = await findAgentIndexPda(memberNumber);
665
+ const registerIx = await getRegisterAgentInstructionAsync({
666
+ payer: { address: ownerAddress },
667
+ owner: ownerAddress,
668
+ groupMint,
669
+ agentMint,
670
+ agentTokenAccount,
671
+ agentIndex,
672
+ name: this._registrationFile.name,
673
+ symbol: "",
674
+ uri,
675
+ additionalMetadata: null,
676
+ nonTransferable: false
677
+ });
678
+ return {
679
+ signature: await access.sender.signAndSend([registerIx], [agentMint]),
680
+ agentId: this._storeIdentity(agentMint.address, ownerAddress, uri, memberNumber)
681
+ };
682
+ } catch (error) {
683
+ lastError = error;
684
+ if (!isRegistrationCollisionError(error)) throw error;
685
+ if (attempt < MAX_RETRIES - 1) await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
686
+ }
687
+ throw new Error(`Registration failed after ${MAX_RETRIES} attempts`, { cause: lastError });
679
688
  }
680
689
  _storeIdentity(mint, owner, uri, memberNumber) {
681
690
  this._identity = {
@@ -708,6 +717,11 @@ var SatiAgent = class SatiAgent {
708
717
  return oasfEndpoint;
709
718
  }
710
719
  };
720
+ /** Detect PDA collision errors from concurrent registrations. */
721
+ function isRegistrationCollisionError(error) {
722
+ const msg = error instanceof Error ? error.message : String(error);
723
+ return msg.includes("already in use") || msg.includes("already been initialized") || msg.includes("0x0");
724
+ }
711
725
 
712
726
  //#endregion
713
727
  //#region src/sdk.ts
@@ -850,7 +864,7 @@ var SatiSDK = class {
850
864
  sasSchema: schema,
851
865
  agentMint: agent.mint
852
866
  })).items.map((item) => {
853
- if (item.data.contentType === ContentType$1.JSON && item.data.content.length > 0) return JSON.parse(new TextDecoder().decode(item.data.content))?.score;
867
+ return this._parseContentJson(item.data.content, item.data.contentType)?.score;
854
868
  }).filter((s) => s !== void 0);
855
869
  const count = scores.length;
856
870
  const averageValue = count > 0 ? scores.reduce((a, b) => a + b, 0) / count : 0;
@@ -858,7 +872,14 @@ var SatiSDK = class {
858
872
  count,
859
873
  averageValue
860
874
  });
861
- } catch {}
875
+ } catch (error) {
876
+ this._warn({
877
+ code: "RPC_ERROR",
878
+ message: "Failed to fetch feedback stats",
879
+ context: agent.mint,
880
+ cause: error
881
+ });
882
+ }
862
883
  }));
863
884
  }
864
885
  const results = [];
@@ -975,7 +996,7 @@ var SatiSDK = class {
975
996
  const transferIx = getTransferInstruction({
976
997
  source: sourceAta,
977
998
  destination: destAta,
978
- authority: { address: ownerAddr },
999
+ authority: ownerAddr,
979
1000
  amount: 1n
980
1001
  });
981
1002
  return { signature: await access.sender.signAndSend([createAtaIx, transferIx]) };
@@ -1199,13 +1220,19 @@ var SatiSDK = class {
1199
1220
  satiFilter.agentMint = knownIdentity.mint;
1200
1221
  }
1201
1222
  if (filters.reviewers?.length) satiFilter.counterparty = address(filters.reviewers[0]);
1223
+ const reviewerSet = filters.reviewers?.length ? new Set(filters.reviewers) : null;
1224
+ const agentMintSet = filters.agents?.length && filters.agents.length > 1 ? new Set(await Promise.all(filters.agents.map(async (id) => {
1225
+ return (await this._resolveIdentity(id)).mint;
1226
+ }))) : null;
1202
1227
  const result = await this._sati.listFeedbacks(satiFilter);
1203
1228
  const currentSlot = await this._sati.getRpc().getSlot({ commitment: "confirmed" }).send();
1204
1229
  const nowSec = Math.floor(Date.now() / 1e3);
1205
1230
  const feedbacks = [];
1206
1231
  for (let i = 0; i < result.items.length; i++) {
1207
1232
  const item = result.items[i];
1208
- const rawContent = item.data.contentType === ContentType$1.JSON && item.data.content.length > 0 ? JSON.parse(new TextDecoder().decode(item.data.content)) : null;
1233
+ if (reviewerSet && !reviewerSet.has(item.data.counterparty)) continue;
1234
+ if (agentMintSet && !agentMintSet.has(item.data.agentMint)) continue;
1235
+ const rawContent = this._parseContentJson(item.data.content, item.data.contentType);
1209
1236
  const score = rawContent?.score;
1210
1237
  const tags = rawContent?.tags ?? [];
1211
1238
  const text = rawContent?.m;
@@ -1263,7 +1290,14 @@ var SatiSDK = class {
1263
1290
  if (!addr) return;
1264
1291
  try {
1265
1292
  fb.txHash = (await photon.getCompressionSignaturesForAddress(address(addr), { limit: 1 })).items[0]?.signature;
1266
- } catch {}
1293
+ } catch (error) {
1294
+ this._warn({
1295
+ code: "SIGNATURE_LOOKUP_FAILED",
1296
+ message: "Failed to fetch tx signature",
1297
+ context: addr,
1298
+ cause: error
1299
+ });
1300
+ }
1267
1301
  }));
1268
1302
  }
1269
1303
  return feedbacks;
@@ -1296,7 +1330,7 @@ var SatiSDK = class {
1296
1330
  let sum = 0;
1297
1331
  let count = 0;
1298
1332
  for (const item of result.items) {
1299
- const rawContent = item.data.contentType === ContentType$1.JSON && item.data.content.length > 0 ? JSON.parse(new TextDecoder().decode(item.data.content)) : null;
1333
+ const rawContent = this._parseContentJson(item.data.content, item.data.contentType);
1300
1334
  const score = rawContent?.score;
1301
1335
  const tags = rawContent?.tags ?? [];
1302
1336
  if (tag1 && !tags.includes(tag1)) continue;
@@ -1336,7 +1370,13 @@ var SatiSDK = class {
1336
1370
  async getCreationSignature(compressedAddress) {
1337
1371
  try {
1338
1372
  return (await this._sati.getLightClient().getRpc().getCompressionSignaturesForAddress(address(compressedAddress), { limit: 1 })).items[0]?.signature ?? null;
1339
- } catch {
1373
+ } catch (error) {
1374
+ this._warn({
1375
+ code: "RPC_ERROR",
1376
+ message: "Failed to fetch creation signature",
1377
+ context: compressedAddress,
1378
+ cause: error
1379
+ });
1340
1380
  return null;
1341
1381
  }
1342
1382
  }
@@ -1370,6 +1410,25 @@ var SatiSDK = class {
1370
1410
  });
1371
1411
  }
1372
1412
  /**
1413
+ * Revoke (close) a feedback by its compressed account address.
1414
+ *
1415
+ * Preferred over `revokeFeedback()` - uses a stable identifier instead of
1416
+ * a fragile array index. Get the address from `feedback.context.satiCompressedAddress`.
1417
+ *
1418
+ * @param compressedAddress - Base58 compressed account address
1419
+ */
1420
+ async revokeFeedbackByAddress(compressedAddress) {
1421
+ const sasConfig = this._requireSASConfig();
1422
+ const signer = this._requireSigner();
1423
+ return this._sati.closeCompressedAttestation({
1424
+ payer: signer,
1425
+ counterparty: signer,
1426
+ sasSchema: sasConfig.schemas.feedbackPublic ?? sasConfig.schemas.feedback,
1427
+ attestationAddress: address(compressedAddress),
1428
+ lookupTableAddress: sasConfig.lookupTable
1429
+ });
1430
+ }
1431
+ /**
1373
1432
  * Search validation attestations for an agent.
1374
1433
  *
1375
1434
  * Validations are on-chain attestations from validators (TEE, zkML, re-execution, etc.)
@@ -1418,6 +1477,19 @@ var SatiSDK = class {
1418
1477
  get config() {
1419
1478
  return this._config;
1420
1479
  }
1480
+ /** Safely parse JSON content from an attestation. Returns null on invalid JSON. */
1481
+ _parseContentJson(content, contentType) {
1482
+ if (contentType !== ContentType$1.JSON || content.length === 0) return null;
1483
+ try {
1484
+ return JSON.parse(new TextDecoder().decode(content));
1485
+ } catch {
1486
+ return null;
1487
+ }
1488
+ }
1489
+ /** Fire a non-fatal warning via the optional onWarning callback. */
1490
+ _warn(warning) {
1491
+ this._config.onWarning?.(warning);
1492
+ }
1421
1493
  _requireSigner() {
1422
1494
  if (!this._config.signer) throw new Error("This operation requires a KeyPairSigner. Initialize SatiSDK with a signer for server-side write operations.");
1423
1495
  return this._config.signer;