@beeperbot/sdk 0.2.1 → 0.2.3

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
@@ -904,8 +904,16 @@ var ENDPOINTS = {
904
904
  PREVIEW: "/agent/preview",
905
905
  BULK_INTENT: "/agent/bulk-intent"
906
906
  };
907
- var AgentClient = class {
907
+ var AgentClient = class _AgentClient {
908
908
  http;
909
+ static CHAIN_ID_MAP = {
910
+ base: 8453,
911
+ ethereum: 1,
912
+ mainnet: 1,
913
+ arbitrum: 42161,
914
+ polygon: 137,
915
+ optimism: 10
916
+ };
909
917
  constructor(config) {
910
918
  if (!config.apiKey) {
911
919
  throw BeeperError.validation("API key is required");
@@ -920,6 +928,62 @@ var AgentClient = class {
920
928
  });
921
929
  this.http = new HttpClient(httpConfig);
922
930
  }
931
+ /**
932
+ * Normalize filters for agent endpoints (accepts flexible shapes from other builders)
933
+ */
934
+ normalizeFilters(filters) {
935
+ const normalized = { ...filters };
936
+ const stripEmptyArray = (key) => {
937
+ const val = normalized[key];
938
+ if (Array.isArray(val) && val.length === 0) {
939
+ delete normalized[key];
940
+ }
941
+ };
942
+ const stripZero = (key) => {
943
+ const val = normalized[key];
944
+ if (typeof val === "number" && val === 0) {
945
+ delete normalized[key];
946
+ }
947
+ };
948
+ if (normalized.platform === "all") delete normalized.platform;
949
+ if (normalized.spamLabel === "all") delete normalized.spamLabel;
950
+ stripZero("minFollowers");
951
+ stripZero("maxFollowers");
952
+ stripZero("activeInLastDays");
953
+ stripZero("minBatteryPercentage");
954
+ stripZero("hasRechargedInLastDays");
955
+ stripZero("maxAttentionPriceUsd");
956
+ if (normalized.neynarScoreMin === 0) delete normalized.neynarScoreMin;
957
+ if (normalized.neynarScoreMax === 1) delete normalized.neynarScoreMax;
958
+ if (normalized.quotientScoreMin === 0) delete normalized.quotientScoreMin;
959
+ if (normalized.quotientScoreMax === 1) delete normalized.quotientScoreMax;
960
+ if (Array.isArray(normalized.countries)) {
961
+ normalized.countries = normalized.countries.map((c) => typeof c === "string" ? c : c?.code).filter((c) => typeof c === "string" && c.trim().length > 0).map((c) => c.trim().toUpperCase());
962
+ }
963
+ stripEmptyArray("countries");
964
+ if (Array.isArray(normalized.tokenHolders)) {
965
+ normalized.tokenHolders = normalized.tokenHolders.map((holder) => {
966
+ if (!holder) return null;
967
+ if ("tokenAddress" in holder && "chainId" in holder) return holder;
968
+ const contractAddress = holder.contractAddress;
969
+ const chain = holder.chain;
970
+ const minBalance = holder.minBalance;
971
+ if (!contractAddress || chain == null) return null;
972
+ const chainId = typeof chain === "number" ? chain : _AgentClient.CHAIN_ID_MAP[String(chain).toLowerCase()] ?? Number(chain);
973
+ if (!Number.isFinite(chainId)) return null;
974
+ return {
975
+ tokenAddress: contractAddress,
976
+ chainId,
977
+ ...minBalance ? { minBalance } : {}
978
+ };
979
+ }).filter((holder) => !!holder);
980
+ }
981
+ stripEmptyArray("tokenHolders");
982
+ stripEmptyArray("signalTokens");
983
+ stripEmptyArray("fids");
984
+ stripEmptyArray("userIds");
985
+ return normalized;
986
+ }
923
987
  /**
924
988
  * Look up a user by username, FID, or wallet address
925
989
  *
@@ -1050,7 +1114,7 @@ var AgentClient = class {
1050
1114
  const response = await this.http.post(
1051
1115
  ENDPOINTS.ESTIMATE,
1052
1116
  {
1053
- filters: input.filters,
1117
+ filters: this.normalizeFilters(input.filters),
1054
1118
  budgetUsd: budgetStr,
1055
1119
  message: input.message
1056
1120
  }
@@ -1080,7 +1144,7 @@ var AgentClient = class {
1080
1144
  const response = await this.http.post(
1081
1145
  ENDPOINTS.PREVIEW,
1082
1146
  {
1083
- filters: input.filters,
1147
+ filters: this.normalizeFilters(input.filters),
1084
1148
  limit
1085
1149
  }
1086
1150
  );
@@ -1112,7 +1176,7 @@ var AgentClient = class {
1112
1176
  const response = await this.http.post(
1113
1177
  ENDPOINTS.BULK_INTENT,
1114
1178
  {
1115
- filters: input.filters,
1179
+ filters: this.normalizeFilters(input.filters),
1116
1180
  budgetUsd: budgetStr,
1117
1181
  message: input.message,
1118
1182
  chainId: input.chainId ?? 8453
@@ -1156,6 +1220,530 @@ var AgentClient = class {
1156
1220
  }
1157
1221
  };
1158
1222
 
1223
+ // src/core/agent/errors.ts
1224
+ var AgentCoreError = class _AgentCoreError extends Error {
1225
+ code;
1226
+ constructor(code, message) {
1227
+ super(message);
1228
+ this.name = "AgentCoreError";
1229
+ this.code = code;
1230
+ }
1231
+ static validation(message) {
1232
+ return new _AgentCoreError("VALIDATION_ERROR", message);
1233
+ }
1234
+ static notFound(message) {
1235
+ return new _AgentCoreError("NOT_FOUND", message);
1236
+ }
1237
+ static unsupportedChain(chainId) {
1238
+ return new _AgentCoreError("UNSUPPORTED_CHAIN", `Unsupported chain: ${chainId}`);
1239
+ }
1240
+ static noRecipients(message) {
1241
+ return new _AgentCoreError("NO_RECIPIENTS", message);
1242
+ }
1243
+ };
1244
+
1245
+ // src/core/agent/filters.ts
1246
+ var CHAIN_ID_MAP = {
1247
+ base: 8453,
1248
+ ethereum: 1,
1249
+ mainnet: 1,
1250
+ arbitrum: 42161,
1251
+ polygon: 137,
1252
+ optimism: 10
1253
+ };
1254
+ function hasSubstantiveFilters(filters) {
1255
+ return !!(filters.platform || filters.minFollowers || filters.maxFollowers || filters.fids?.length || filters.userIds?.length || filters.activeInLastDays || filters.countries?.length || filters.neynarScoreMin || filters.tokenHolders?.length || filters.maxAttentionPriceUsd || filters.hasBaseWallet || filters.spamLabel);
1256
+ }
1257
+ function normalizeAgentFilters(filters) {
1258
+ const normalized = { ...filters };
1259
+ const defaultsApplied = [];
1260
+ const stripEmptyArray = (key) => {
1261
+ const value = normalized[key];
1262
+ if (Array.isArray(value) && value.length === 0) {
1263
+ delete normalized[key];
1264
+ }
1265
+ };
1266
+ const stripZero = (key) => {
1267
+ const value = normalized[key];
1268
+ if (typeof value === "number" && value === 0) {
1269
+ delete normalized[key];
1270
+ }
1271
+ };
1272
+ if (normalized.platform === "all") delete normalized.platform;
1273
+ if (normalized.spamLabel === "all") delete normalized.spamLabel;
1274
+ stripZero("minFollowers");
1275
+ stripZero("maxFollowers");
1276
+ stripZero("activeInLastDays");
1277
+ stripZero("minBatteryPercentage");
1278
+ stripZero("hasRechargedInLastDays");
1279
+ stripZero("maxAttentionPriceUsd");
1280
+ stripEmptyArray("signalTokens");
1281
+ if (Array.isArray(normalized.tokenHolders)) {
1282
+ normalized.tokenHolders = normalized.tokenHolders.map((holder) => {
1283
+ if (!holder) return null;
1284
+ if ("tokenAddress" in holder && "chainId" in holder) return holder;
1285
+ const loose = holder;
1286
+ if (!loose.contractAddress || loose.chain == null) return null;
1287
+ const chainId = typeof loose.chain === "number" ? loose.chain : CHAIN_ID_MAP[String(loose.chain).toLowerCase()] ?? Number(loose.chain);
1288
+ if (!Number.isFinite(chainId)) return null;
1289
+ return {
1290
+ tokenAddress: loose.contractAddress,
1291
+ chainId,
1292
+ ...loose.minBalance ? { minBalance: loose.minBalance } : {}
1293
+ };
1294
+ }).filter((holder) => !!holder);
1295
+ }
1296
+ stripEmptyArray("tokenHolders");
1297
+ if (Array.isArray(normalized.countries)) {
1298
+ normalized.countries = normalized.countries.map((country) => {
1299
+ if (typeof country === "string") return country;
1300
+ return country?.code;
1301
+ }).filter((country) => typeof country === "string" && country.length > 0).map((country) => country.toUpperCase());
1302
+ }
1303
+ stripEmptyArray("countries");
1304
+ stripEmptyArray("fids");
1305
+ stripEmptyArray("userIds");
1306
+ if (normalized.neynarScoreMin === 0) delete normalized.neynarScoreMin;
1307
+ if (normalized.neynarScoreMax === 1) delete normalized.neynarScoreMax;
1308
+ if (normalized.quotientScoreMin === 0) delete normalized.quotientScoreMin;
1309
+ if (normalized.quotientScoreMax === 1) delete normalized.quotientScoreMax;
1310
+ if (!hasSubstantiveFilters(normalized)) {
1311
+ normalized.activeInLastDays = 30;
1312
+ normalized.spamLabel = "not_spam_only";
1313
+ defaultsApplied.push("activeInLastDays=30", "spamLabel=not_spam_only");
1314
+ }
1315
+ return { filters: normalized, defaultsApplied };
1316
+ }
1317
+ function getOrderByOrDefault(filters) {
1318
+ return filters.orderBy ?? "attention_price_asc";
1319
+ }
1320
+ function buildAgentQueryPlan(filters) {
1321
+ const normalized = normalizeAgentFilters(filters);
1322
+ return {
1323
+ inputFilters: { ...filters },
1324
+ filters: normalized.filters,
1325
+ orderBy: getOrderByOrDefault(normalized.filters),
1326
+ defaultsApplied: normalized.defaultsApplied
1327
+ };
1328
+ }
1329
+
1330
+ // src/core/agent/costing.ts
1331
+ var DEFAULT_MICRO_USD_PER_USD = 1e6;
1332
+ var DEFAULT_PLATFORM_FEE_RATE = 0.1;
1333
+ var GOAL_BASELINES = {
1334
+ max_reach: { payout: 100, bonus: 0 },
1335
+ lil_mission: { payout: 80, bonus: 20 },
1336
+ hard_mission: { payout: 50, bonus: 50 }
1337
+ };
1338
+ var SPLIT_ADJUSTMENTS = {
1339
+ balanced: 0,
1340
+ more_reach: 10,
1341
+ more_action: -10
1342
+ };
1343
+ function computeBudgetSplit(totalUsd, goalType = "max_reach", splitPreset = "balanced", platformFeeRate = DEFAULT_PLATFORM_FEE_RATE) {
1344
+ const baseline = GOAL_BASELINES[goalType];
1345
+ const adjustment = SPLIT_ADJUSTMENTS[splitPreset];
1346
+ let payoutPercent = Math.max(0, Math.min(100, baseline.payout + adjustment));
1347
+ let bonusPercent = Math.max(0, Math.min(100, baseline.bonus - adjustment));
1348
+ const sum = payoutPercent + bonusPercent;
1349
+ if (sum !== 100) {
1350
+ payoutPercent = payoutPercent / sum * 100;
1351
+ bonusPercent = bonusPercent / sum * 100;
1352
+ }
1353
+ const payoutUsd = roundToCents(totalUsd * payoutPercent / 100);
1354
+ const bonusUsd = roundToCents(totalUsd * bonusPercent / 100);
1355
+ const platformFeeUsd = roundToCents(totalUsd * platformFeeRate);
1356
+ const userPaysUsd = roundToCents(totalUsd + platformFeeUsd);
1357
+ return {
1358
+ totalUsd,
1359
+ payoutUsd,
1360
+ bonusUsd,
1361
+ platformFeeUsd,
1362
+ userPaysUsd,
1363
+ goalType,
1364
+ splitPreset,
1365
+ payoutPercent: Math.round(payoutPercent),
1366
+ bonusPercent: Math.round(bonusPercent)
1367
+ };
1368
+ }
1369
+ function roundToCents(value) {
1370
+ return Math.round(value * 100) / 100;
1371
+ }
1372
+ function parseUsd(value) {
1373
+ return parseFloat(value.replace(/^\$/, "").trim());
1374
+ }
1375
+ function usdToMicroUsd(usd, microUsdPerUsd = DEFAULT_MICRO_USD_PER_USD) {
1376
+ return Math.round(usd * microUsdPerUsd);
1377
+ }
1378
+
1379
+ // src/core/agent/engine.ts
1380
+ var DEFAULT_CHAIN_INFO = {
1381
+ 8453: { name: "Base", usdcAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" }
1382
+ };
1383
+ var AgentCore = class {
1384
+ adapter;
1385
+ defaultChainId;
1386
+ platformFeeRate;
1387
+ estimateMaxUsers;
1388
+ bulkMaxUsers;
1389
+ microUsdPerUsd;
1390
+ chainInfo;
1391
+ constructor(adapter, config = {}) {
1392
+ this.adapter = adapter;
1393
+ this.defaultChainId = config.defaultChainId ?? 8453;
1394
+ this.platformFeeRate = config.platformFeeRate ?? DEFAULT_PLATFORM_FEE_RATE;
1395
+ this.estimateMaxUsers = config.estimateMaxUsers ?? 1e4;
1396
+ this.bulkMaxUsers = config.bulkMaxUsers ?? 1e4;
1397
+ this.microUsdPerUsd = config.microUsdPerUsd ?? DEFAULT_MICRO_USD_PER_USD;
1398
+ this.chainInfo = config.chainInfo ?? DEFAULT_CHAIN_INFO;
1399
+ }
1400
+ buildQueryPlan(filters) {
1401
+ return buildAgentQueryPlan(filters);
1402
+ }
1403
+ normalizeFilters(filters) {
1404
+ return normalizeAgentFilters(filters);
1405
+ }
1406
+ computeBudgetSplit(totalUsd, goalType = "max_reach", splitPreset = "balanced") {
1407
+ return computeBudgetSplit(totalUsd, goalType, splitPreset, this.platformFeeRate);
1408
+ }
1409
+ async lookup(query) {
1410
+ const user = await this.adapter.resolveUser(query);
1411
+ if (!user) return null;
1412
+ const fid = user.fid ?? await this.adapter.getFidForUser(user.id) ?? void 0;
1413
+ const walletAddress = user.walletAddress ?? await this.adapter.getPrimaryWalletAddress(user.id) ?? void 0;
1414
+ return {
1415
+ id: user.id,
1416
+ username: user.username,
1417
+ platform: user.platform,
1418
+ attentionPriceUsd: (Number(user.attentionPriceMicroUsd) / this.microUsdPerUsd).toFixed(2),
1419
+ ...fid !== void 0 ? { fid } : {},
1420
+ ...user.displayName ? { displayName: user.displayName } : {},
1421
+ ...user.pfpUrl ? { pfpUrl: user.pfpUrl } : {},
1422
+ ...walletAddress ? { walletAddress } : {},
1423
+ ...user.followerCount !== void 0 ? { followerCount: user.followerCount } : {}
1424
+ };
1425
+ }
1426
+ async getPrice(query) {
1427
+ const user = await this.lookup(query);
1428
+ if (!user) return null;
1429
+ const priceUsdNum = parseFloat(user.attentionPriceUsd);
1430
+ return {
1431
+ userId: user.id,
1432
+ username: user.username,
1433
+ priceUsd: user.attentionPriceUsd,
1434
+ priceUsdc: usdToMicroUsd(priceUsdNum, this.microUsdPerUsd).toString()
1435
+ };
1436
+ }
1437
+ async prepareBeep(params) {
1438
+ const user = await this.lookup(params.to);
1439
+ if (!user) {
1440
+ return { error: "user_not_found", message: `User not found: ${params.to}` };
1441
+ }
1442
+ if (!user.walletAddress) {
1443
+ return { error: "no_wallet", message: `@${user.username} has no connected wallet` };
1444
+ }
1445
+ const baseAmount = parseUsd(params.amountUsd);
1446
+ if (Number.isNaN(baseAmount) || baseAmount <= 0) {
1447
+ return { error: "invalid_amount", message: "Amount must be a positive number" };
1448
+ }
1449
+ const attentionPrice = parseFloat(user.attentionPriceUsd);
1450
+ if (baseAmount < attentionPrice) {
1451
+ return {
1452
+ error: "below_minimum",
1453
+ message: `Amount $${baseAmount.toFixed(2)} is below @${user.username}'s minimum beep price of $${attentionPrice.toFixed(2)}.`,
1454
+ minimumUsd: user.attentionPriceUsd,
1455
+ requestedUsd: params.amountUsd
1456
+ };
1457
+ }
1458
+ const chainId = params.chainId ?? this.defaultChainId;
1459
+ const chain = this.chainInfo[chainId];
1460
+ if (!chain) {
1461
+ return { error: "unsupported_chain", message: `Chain ${chainId} is not supported` };
1462
+ }
1463
+ const goalType = params.goalType ?? (params.bonusConfig ? "lil_mission" : "max_reach");
1464
+ const hasMissions = goalType !== "max_reach";
1465
+ const splitPreset = params.splitPreset ?? "balanced";
1466
+ const split = hasMissions ? this.computeBudgetSplit(baseAmount, goalType, splitPreset) : void 0;
1467
+ const platformFeeUsd = Math.round(baseAmount * this.platformFeeRate * 100) / 100;
1468
+ const totalAmountUsd = baseAmount + platformFeeUsd;
1469
+ const result = {
1470
+ type: "confirmation",
1471
+ recipient: {
1472
+ username: user.username,
1473
+ walletAddress: user.walletAddress,
1474
+ ...user.displayName ? { displayName: user.displayName } : {},
1475
+ ...user.pfpUrl ? { pfpUrl: user.pfpUrl } : {},
1476
+ ...user.fid !== void 0 ? { fid: user.fid } : {}
1477
+ },
1478
+ message: params.message,
1479
+ baseAmountUsd: baseAmount.toFixed(2),
1480
+ platformFeeUsd: platformFeeUsd.toFixed(2),
1481
+ totalAmountUsd: totalAmountUsd.toFixed(2),
1482
+ feePercentage: this.platformFeeRate * 100,
1483
+ chainId,
1484
+ chainName: chain.name,
1485
+ attentionPriceUsd: user.attentionPriceUsd
1486
+ };
1487
+ if (hasMissions && split) {
1488
+ result.hasMissions = true;
1489
+ result.budgetSplit = split;
1490
+ if (params.bonusConfig) {
1491
+ result.bonusConfig = params.bonusConfig;
1492
+ }
1493
+ }
1494
+ return result;
1495
+ }
1496
+ async createIntent(params) {
1497
+ const user = await this.lookup(params.to);
1498
+ if (!user || !user.walletAddress) return null;
1499
+ const baseAmountUsd = parseUsd(params.amountUsd);
1500
+ if (Number.isNaN(baseAmountUsd) || baseAmountUsd <= 0) {
1501
+ throw AgentCoreError.validation("Invalid amount");
1502
+ }
1503
+ const chainId = params.chainId ?? this.defaultChainId;
1504
+ const chain = this.chainInfo[chainId];
1505
+ if (!chain) throw AgentCoreError.unsupportedChain(chainId);
1506
+ const platformFeeUsd = Math.round(baseAmountUsd * this.platformFeeRate * 100) / 100;
1507
+ const totalAmountUsd = baseAmountUsd + platformFeeUsd;
1508
+ const amountUsdc = usdToMicroUsd(totalAmountUsd, this.microUsdPerUsd).toString();
1509
+ const intentId = `intent_${randomUUID()}`;
1510
+ const shortAddress = `${user.walletAddress.slice(0, 6)}...${user.walletAddress.slice(-4)}`;
1511
+ const message = params.message ?? "";
1512
+ return {
1513
+ intentId,
1514
+ recipient: user.walletAddress,
1515
+ amount: amountUsdc,
1516
+ amountFormatted: `${totalAmountUsd.toFixed(2)} USDC`,
1517
+ tokenAddress: chain.usdcAddress,
1518
+ tokenSymbol: "USDC",
1519
+ chainId,
1520
+ chainName: chain.name,
1521
+ message,
1522
+ recipientInfo: {
1523
+ username: user.username,
1524
+ ...user.displayName ? { displayName: user.displayName } : {},
1525
+ ...user.fid !== void 0 ? { fid: user.fid } : {}
1526
+ },
1527
+ instruction: `Send ${totalAmountUsd.toFixed(2)} USDC to ${shortAddress} on ${chain.name} (${baseAmountUsd.toFixed(2)} to @${user.username} + ${platformFeeUsd.toFixed(2)} platform fee)`,
1528
+ expiresAt: new Date(Date.now() + 60 * 60 * 1e3).toISOString(),
1529
+ baseAmountUsd: baseAmountUsd.toFixed(2),
1530
+ platformFeeUsd: platformFeeUsd.toFixed(2),
1531
+ totalAmountUsd: totalAmountUsd.toFixed(2),
1532
+ feePercentage: this.platformFeeRate * 100
1533
+ };
1534
+ }
1535
+ async estimate(params) {
1536
+ const budgetUsd = parseUsd(params.budgetUsd);
1537
+ if (Number.isNaN(budgetUsd) || budgetUsd <= 0) {
1538
+ throw AgentCoreError.validation("Budget must be a positive number");
1539
+ }
1540
+ const plan = this.buildQueryPlan(params.filters);
1541
+ const candidates = await this.adapter.findUsers({
1542
+ filters: plan.filters,
1543
+ orderBy: plan.orderBy,
1544
+ limit: this.estimateMaxUsers
1545
+ });
1546
+ const budgetMicro = usdToMicroUsd(budgetUsd, this.microUsdPerUsd);
1547
+ let totalCost = 0;
1548
+ let recipientCount = 0;
1549
+ let minPrice = Number.POSITIVE_INFINITY;
1550
+ let maxPrice = 0;
1551
+ for (const candidate of candidates) {
1552
+ const priceMicro = Number(candidate.attentionPriceMicroUsd);
1553
+ if (totalCost + priceMicro > budgetMicro) break;
1554
+ totalCost += priceMicro;
1555
+ recipientCount += 1;
1556
+ if (priceMicro < minPrice) minPrice = priceMicro;
1557
+ if (priceMicro > maxPrice) maxPrice = priceMicro;
1558
+ }
1559
+ const avgPrice = recipientCount > 0 ? totalCost / recipientCount : 0;
1560
+ return {
1561
+ recipientCount,
1562
+ totalCostUsd: (totalCost / this.microUsdPerUsd).toFixed(2),
1563
+ avgPriceUsd: (avgPrice / this.microUsdPerUsd).toFixed(2),
1564
+ minPriceUsd: recipientCount > 0 ? (minPrice / this.microUsdPerUsd).toFixed(2) : "0.00",
1565
+ maxPriceUsd: recipientCount > 0 ? (maxPrice / this.microUsdPerUsd).toFixed(2) : "0.00",
1566
+ remainingBudgetUsd: ((budgetMicro - totalCost) / this.microUsdPerUsd).toFixed(2),
1567
+ budgetSufficient: recipientCount > 0,
1568
+ diagnostics: {
1569
+ candidateCount: candidates.length,
1570
+ budgetMicro,
1571
+ appliedDefaults: plan.defaultsApplied
1572
+ }
1573
+ };
1574
+ }
1575
+ async preview(params) {
1576
+ const limit = Math.min(Math.max(params.limit, 1), 20);
1577
+ const plan = this.buildQueryPlan(params.filters);
1578
+ const [users, totalCount] = await Promise.all([
1579
+ this.adapter.findUsers({
1580
+ filters: plan.filters,
1581
+ orderBy: plan.orderBy,
1582
+ limit,
1583
+ includeProfile: true,
1584
+ includeReputation: true
1585
+ }),
1586
+ this.adapter.countUsers(plan.filters)
1587
+ ]);
1588
+ const userIds = users.map((user) => user.id);
1589
+ const fidMap = await this.adapter.getBulkFidsForUsers(userIds);
1590
+ return {
1591
+ users: users.map((user) => {
1592
+ const previewUser = {
1593
+ username: user.username,
1594
+ priceUsd: (Number(user.attentionPriceMicroUsd) / this.microUsdPerUsd).toFixed(2),
1595
+ ...user.displayName ? { displayName: user.displayName } : {},
1596
+ ...user.followerCount !== void 0 ? { followerCount: user.followerCount } : {},
1597
+ ...user.neynarScore !== void 0 ? { neynarScore: user.neynarScore } : {},
1598
+ ...user.pfpUrl ? { pfpUrl: user.pfpUrl } : {}
1599
+ };
1600
+ const fid = fidMap.get(user.id);
1601
+ if (fid !== void 0) {
1602
+ return { ...previewUser, fid };
1603
+ }
1604
+ return previewUser;
1605
+ }),
1606
+ totalCount,
1607
+ hasMore: totalCount > limit
1608
+ };
1609
+ }
1610
+ async createBulkIntent(params) {
1611
+ const budgetUsd = parseUsd(params.budgetUsd);
1612
+ if (Number.isNaN(budgetUsd) || budgetUsd <= 0) {
1613
+ throw AgentCoreError.validation("Budget must be a positive number");
1614
+ }
1615
+ const chainId = params.chainId ?? this.defaultChainId;
1616
+ const chain = this.chainInfo[chainId];
1617
+ if (!chain) throw AgentCoreError.unsupportedChain(chainId);
1618
+ const plan = this.buildQueryPlan(params.filters);
1619
+ const users = await this.adapter.findUsers({
1620
+ filters: plan.filters,
1621
+ orderBy: "attention_price_asc",
1622
+ limit: this.bulkMaxUsers
1623
+ });
1624
+ const budgetMicro = usdToMicroUsd(budgetUsd, this.microUsdPerUsd);
1625
+ let totalAmountMicro = 0;
1626
+ const selectedUsers = [];
1627
+ for (const user of users) {
1628
+ const priceMicro = Number(user.attentionPriceMicroUsd);
1629
+ if (totalAmountMicro + priceMicro > budgetMicro) break;
1630
+ totalAmountMicro += priceMicro;
1631
+ selectedUsers.push({
1632
+ id: user.id,
1633
+ username: user.username,
1634
+ amountMicro: priceMicro
1635
+ });
1636
+ }
1637
+ if (selectedUsers.length === 0) {
1638
+ throw AgentCoreError.noRecipients("No eligible recipients within budget");
1639
+ }
1640
+ const walletMap = await this.adapter.getBulkPrimaryWalletAddresses(selectedUsers.map((u) => u.id));
1641
+ const payments = selectedUsers.filter((user) => walletMap.has(user.id)).map((user) => ({
1642
+ recipient: walletMap.get(user.id),
1643
+ amount: user.amountMicro.toString(),
1644
+ username: user.username
1645
+ }));
1646
+ if (payments.length === 0) {
1647
+ throw AgentCoreError.noRecipients("No recipients with valid wallets found");
1648
+ }
1649
+ const totalAmount = payments.reduce((sum, payment) => sum + BigInt(payment.amount), 0n);
1650
+ const totalFormatted = (Number(totalAmount) / this.microUsdPerUsd).toFixed(2);
1651
+ return {
1652
+ bulkIntentId: `bulk_${randomUUID()}`,
1653
+ recipientCount: payments.length,
1654
+ totalAmount: totalAmount.toString(),
1655
+ totalAmountFormatted: `${totalFormatted} USDC`,
1656
+ payments,
1657
+ summary: `Send to ${payments.length} recipients for total ${totalFormatted} USDC on ${chain.name}`,
1658
+ chainId,
1659
+ chainName: chain.name,
1660
+ tokenAddress: chain.usdcAddress,
1661
+ tokenSymbol: "USDC",
1662
+ expiresAt: new Date(Date.now() + 60 * 60 * 1e3).toISOString()
1663
+ };
1664
+ }
1665
+ async prepareCampaign(params) {
1666
+ const totalBudget = parseUsd(params.budgetUsd);
1667
+ if (Number.isNaN(totalBudget) || totalBudget <= 0) {
1668
+ throw AgentCoreError.validation("Budget must be a positive number");
1669
+ }
1670
+ const chainId = params.chainId ?? this.defaultChainId;
1671
+ const chain = this.chainInfo[chainId];
1672
+ if (!chain) throw AgentCoreError.unsupportedChain(chainId);
1673
+ const goalType = params.goalType ?? (params.bonusConfig ? "lil_mission" : "max_reach");
1674
+ const splitPreset = params.splitPreset ?? "balanced";
1675
+ const split = this.computeBudgetSplit(totalBudget, goalType, splitPreset);
1676
+ const payoutEstimate = await this.estimate({
1677
+ filters: params.filters,
1678
+ budgetUsd: split.payoutUsd.toFixed(2),
1679
+ ...params.message ? { message: params.message } : {}
1680
+ });
1681
+ let missionSummary;
1682
+ if (params.bonusConfig) {
1683
+ const ctaLabels = params.bonusConfig.ctas.map((cta) => {
1684
+ switch (cta.type) {
1685
+ case "follow_profile":
1686
+ return `Follow @${cta.profileUsername || cta.profileFid}`;
1687
+ case "like_cast":
1688
+ return "Like a cast";
1689
+ case "recast":
1690
+ return "Recast";
1691
+ case "share_cast":
1692
+ return "Quote cast";
1693
+ case "visit_link":
1694
+ return `Visit ${cta.label || cta.url || "link"}`;
1695
+ case "quiz":
1696
+ return `Quiz (${cta.questions?.length || 0} questions)`;
1697
+ case "external_verify":
1698
+ return cta.description || "Custom quest";
1699
+ case "x_follow":
1700
+ return `Follow @${cta.handle} on X`;
1701
+ case "x_like":
1702
+ return "Like tweet on X";
1703
+ case "x_recast":
1704
+ return "Repost on X";
1705
+ default:
1706
+ return cta.type;
1707
+ }
1708
+ });
1709
+ const distributionLabel = params.bonusConfig.type === "lottery" ? "BEEPERY (lottery)" : "FCFS (first come)";
1710
+ missionSummary = `${distributionLabel} \u2022 $${split.bonusUsd.toFixed(2)} pool \u2022 ${ctaLabels.join(", ")}`;
1711
+ }
1712
+ return {
1713
+ type: "campaign_confirmation",
1714
+ recipientCount: payoutEstimate.recipientCount,
1715
+ budgetSplit: split,
1716
+ payoutEstimate: {
1717
+ recipientCount: payoutEstimate.recipientCount,
1718
+ totalPayoutCostUsd: payoutEstimate.totalCostUsd,
1719
+ avgPriceUsd: payoutEstimate.avgPriceUsd
1720
+ },
1721
+ platformFeeUsd: split.platformFeeUsd.toFixed(2),
1722
+ userPaysUsd: split.userPaysUsd.toFixed(2),
1723
+ chainId,
1724
+ chainName: chain.name,
1725
+ ...params.bonusConfig ? { bonusConfig: params.bonusConfig } : {},
1726
+ ...missionSummary ? { missionSummary } : {},
1727
+ ...params.message ? { message: params.message } : {}
1728
+ };
1729
+ }
1730
+ async getBeepHistory(params) {
1731
+ return this.adapter.getBeepHistory(params);
1732
+ }
1733
+ };
1734
+ function createAgentCore(adapter, config) {
1735
+ return new AgentCore(adapter, config);
1736
+ }
1737
+ function normalizeFilters(filters) {
1738
+ return normalizeAgentFilters(filters);
1739
+ }
1740
+ function buildQueryPlan(filters) {
1741
+ return buildAgentQueryPlan(filters);
1742
+ }
1743
+ function getDefaultOrderBy(filters) {
1744
+ return buildAgentQueryPlan(filters).orderBy;
1745
+ }
1746
+
1159
1747
  // src/client/auth.ts
1160
1748
  var API_KEY_PREFIXES = {
1161
1749
  LIVE: "bpk_live_",
@@ -1194,6 +1782,7 @@ function maskApiKey(apiKey) {
1194
1782
  }
1195
1783
 
1196
1784
  // src/client/BeeperClient.ts
1785
+ var ALLOWED_REWARD_TYPES = ["guaranteed", "lottery", "fcfs"];
1197
1786
  var QuoteSchema = z.object({
1198
1787
  id: z.string(),
1199
1788
  status: z.enum([
@@ -1400,6 +1989,16 @@ var BeeperClient = class {
1400
1989
  if (input.message.length > 1e3) {
1401
1990
  throw BeeperError.validation("Message must be 1000 characters or less");
1402
1991
  }
1992
+ if (input.rewardType !== void 0) {
1993
+ if (input.rewardType.trim().length === 0) {
1994
+ throw BeeperError.validation("rewardType must be a non-empty string");
1995
+ }
1996
+ if (!ALLOWED_REWARD_TYPES.includes(input.rewardType)) {
1997
+ throw BeeperError.validation(
1998
+ `rewardType must be one of: ${ALLOWED_REWARD_TYPES.join(", ")}`
1999
+ );
2000
+ }
2001
+ }
1403
2002
  if (!input.recipientFids?.length && !input.filter) {
1404
2003
  throw BeeperError.validation("Must provide either recipientFids or filter");
1405
2004
  }
@@ -1418,6 +2017,7 @@ var BeeperClient = class {
1418
2017
  filter: input.filter,
1419
2018
  budgetUSD: input.budgetUSD,
1420
2019
  rewardType: input.rewardType ?? "guaranteed",
2020
+ mode: "attention_marketplace",
1421
2021
  memo: input.memo,
1422
2022
  metadata: input.metadata,
1423
2023
  ttlSeconds: opts?.ttlSeconds ?? 300
@@ -2781,11 +3381,10 @@ var FilterBuilder = class _FilterBuilder {
2781
3381
  throw new Error("minBalance must be a numeric string (wei)");
2782
3382
  }
2783
3383
  }
2784
- const chainNames = { 1: "ethereum", 8453: "base", 42161: "arbitrum", 10: "optimism", 137: "polygon" };
2785
3384
  return new FilterExpression({
2786
3385
  cachedTokenHolders: opts.map((o) => ({
2787
3386
  contractAddress: o.tokenAddress,
2788
- chain: chainNames[o.chainId] ?? String(o.chainId),
3387
+ chain: o.chainId === 8453 ? "base" : "ethereum",
2789
3388
  tokenStandard: o.tokenStandard ?? "ERC20",
2790
3389
  ...o.minBalance !== void 0 ? { minBalance: o.minBalance } : {},
2791
3390
  ...o.tokenId !== void 0 ? { tokenId: o.tokenId } : {}
@@ -3234,6 +3833,6 @@ function parseReceipt(receipt) {
3234
3833
  return ReceiptSchema2.parse(receipt);
3235
3834
  }
3236
3835
 
3237
- export { API_BASE_URLS, API_KEY_PREFIXES, ActiveInLastDaysFilterSchema, AgentClient, ApiQuoteResponseSchema, ApiReceiptResponseSchema, BeeperClient, BeeperEconomicsFilterSchema, BeeperError, CachedTokenHolderFilterSchema, CachedTokenHolderSchema, ConfirmDepositResponseSchema, ConfirmResultSchema2 as ConfirmResultSchema, CountryFilterSchema, DistributionStrategySchema, DraftInputSchema, DraftSchema, DraftStatusSchema, DraftUpdateSchema, ErrorCodes, ExcludePingedTodayFilterSchema, ExcludeUsersFilterSchema, ExecuteResultSchema2 as ExecuteResultSchema, ExecuteSendResponseSchema, ExecuteStatusSchema, FILTER_SCHEMA, FieldComparisonSchema, FilterBuilder, FilterExpression, FilterExpressionSchema, FilterOperatorSchema, FilterValueSchema, FollowersOfFilterSchema, FollowingOfFilterSchema, GasTierSchema, HEADERS, HTTP_STATUS_TO_ERROR_CODE, HasBaseWalletFilterSchema, HasRechargedInLastDaysFilterSchema, HasTierFilterSchema, HasVerifiedWalletFilterSchema, HttpClient, IsWaitlistedFilterSchema, LegacyFieldComparisonSchema, LegacyFilterOperatorSchema, LegacyFilterValueSchema, LegacyRecipientFilterDSLSchema, MaxAttentionPriceFilterSchema, MaxFidFilterSchema, MaxFollowersFilterSchema, MaxFollowingFilterSchema, MinAttentionPriceFilterSchema, MinBatteryPercentageFilterSchema, MinCastCountFilterSchema, MinClickThroughRateFilterSchema, MinFidFilterSchema, MinFollowersFilterSchema, MinFollowingFilterSchema, MinProTenureDaysFilterSchema, MinTenureDaysFilterSchema, MutualsWithFilterSchema, NetworkSchema, NeynarScoreMaxFilterSchema, NeynarScoreMinFilterSchema, OnchainFilterSchema, OrderBySchema, PlatformFilterSchema, ProSubscriptionFilterSchema, QUOTE_EXPIRATION_SECONDS, QuoteOptionsSchema, QuoteRecipientSchema, QuoteSchema2 as QuoteSchema, QuotientScoreMaxFilterSchema, QuotientScoreMinFilterSchema, RETRYABLE_ERROR_CODES, RETRY_CONFIG, ReceiptSchema2 as ReceiptSchema, ReceiptTransactionSchema, ReceiptTransferSchema, RecipientFilterDSLSchema, RecipientFilterSchema, ReputationFilterSchema, RequireLotteryOptInFilterSchema, RequireQuizOptInFilterSchema, RolesFilterSchema, SDK_VERSION, SignalTokenFilterSchema, SocialFilterSchema, SpamLabelFilterSchema, SpecificUsersFilterSchema, TIMEOUTS, TimezoneFilterSchema, TokenHolderDiscoverySchema, TokenHolderFilterSchema, TokenTypeSchema, TransferStatusSchema, VerifiedOnlyFilterSchema, createHttpConfig, BeeperClient_default as default, describeFilters, generateDepositIdempotencyKey, generateExecuteIdempotencyKey, generateFilterDocumentation, generateIdempotencyKey, getAllFilterNames, getApiKeyEnvironment, getFilterSchema, isRetryableCode, isValidApiKeyFormat, maskApiKey, parseDraft, parseDraftInput, parseExecuteResult, parseFilter, parseQuote, parseQuoteOptions, parseReceipt, parseRecipientFilter, safeParseFilter, safeParseRecipientFilter, confirmDeposit as sendConfirmDeposit, createDraft as sendCreateDraft, createQuote as sendCreateQuote, executeSend as sendExecuteSend, getEstimatedTimeRemaining as sendGetEstimatedTimeRemaining, getFailedTransactions as sendGetFailedTransactions, getQuote as sendGetQuote, getReceipt as sendGetReceipt, getSuccessRate as sendGetSuccessRate, isComplete as sendIsComplete, isDepositSufficient as sendIsDepositSufficient, isExecuting as sendIsExecuting, isQuoteExpired as sendIsQuoteExpired, isReadyForDeposit as sendIsReadyForDeposit, isReadyForQuote as sendIsReadyForQuote, isSuccess as sendIsSuccess, pollUntilComplete as sendPollUntilComplete, updateDraft as sendUpdateDraft, validateDraftInput2 as sendValidateDraftInput, validateDraft, validateDraftInput, validateExecuteResult, validateFilter, validateFilterHasTargeting, validateQuote, validateQuoteOptions, validateReceipt, validateRecipientFilter };
3836
+ export { API_BASE_URLS, API_KEY_PREFIXES, ActiveInLastDaysFilterSchema, AgentClient, AgentCore, AgentCoreError, ApiQuoteResponseSchema, ApiReceiptResponseSchema, BeeperClient, BeeperEconomicsFilterSchema, BeeperError, CachedTokenHolderFilterSchema, CachedTokenHolderSchema, ConfirmDepositResponseSchema, ConfirmResultSchema2 as ConfirmResultSchema, CountryFilterSchema, DistributionStrategySchema, DraftInputSchema, DraftSchema, DraftStatusSchema, DraftUpdateSchema, ErrorCodes, ExcludePingedTodayFilterSchema, ExcludeUsersFilterSchema, ExecuteResultSchema2 as ExecuteResultSchema, ExecuteSendResponseSchema, ExecuteStatusSchema, FILTER_SCHEMA, FieldComparisonSchema, FilterBuilder, FilterExpression, FilterExpressionSchema, FilterOperatorSchema, FilterValueSchema, FollowersOfFilterSchema, FollowingOfFilterSchema, GasTierSchema, HEADERS, HTTP_STATUS_TO_ERROR_CODE, HasBaseWalletFilterSchema, HasRechargedInLastDaysFilterSchema, HasTierFilterSchema, HasVerifiedWalletFilterSchema, HttpClient, IsWaitlistedFilterSchema, LegacyFieldComparisonSchema, LegacyFilterOperatorSchema, LegacyFilterValueSchema, LegacyRecipientFilterDSLSchema, MaxAttentionPriceFilterSchema, MaxFidFilterSchema, MaxFollowersFilterSchema, MaxFollowingFilterSchema, MinAttentionPriceFilterSchema, MinBatteryPercentageFilterSchema, MinCastCountFilterSchema, MinClickThroughRateFilterSchema, MinFidFilterSchema, MinFollowersFilterSchema, MinFollowingFilterSchema, MinProTenureDaysFilterSchema, MinTenureDaysFilterSchema, MutualsWithFilterSchema, NetworkSchema, NeynarScoreMaxFilterSchema, NeynarScoreMinFilterSchema, OnchainFilterSchema, OrderBySchema, PlatformFilterSchema, ProSubscriptionFilterSchema, QUOTE_EXPIRATION_SECONDS, QuoteOptionsSchema, QuoteRecipientSchema, QuoteSchema2 as QuoteSchema, QuotientScoreMaxFilterSchema, QuotientScoreMinFilterSchema, RETRYABLE_ERROR_CODES, RETRY_CONFIG, ReceiptSchema2 as ReceiptSchema, ReceiptTransactionSchema, ReceiptTransferSchema, RecipientFilterDSLSchema, RecipientFilterSchema, ReputationFilterSchema, RequireLotteryOptInFilterSchema, RequireQuizOptInFilterSchema, RolesFilterSchema, SDK_VERSION, SignalTokenFilterSchema, SocialFilterSchema, SpamLabelFilterSchema, SpecificUsersFilterSchema, TIMEOUTS, TimezoneFilterSchema, TokenHolderDiscoverySchema, TokenHolderFilterSchema, TokenTypeSchema, TransferStatusSchema, VerifiedOnlyFilterSchema, buildQueryPlan as buildAgentCoreQueryPlan, buildAgentQueryPlan, computeBudgetSplit as computeAgentBudgetSplit, createAgentCore, createHttpConfig, BeeperClient_default as default, describeFilters, generateDepositIdempotencyKey, generateExecuteIdempotencyKey, generateFilterDocumentation, generateIdempotencyKey, getDefaultOrderBy as getAgentCoreDefaultOrderBy, getAllFilterNames, getApiKeyEnvironment, getFilterSchema, getOrderByOrDefault, isRetryableCode, isValidApiKeyFormat, maskApiKey, normalizeFilters as normalizeAgentCoreFilters, normalizeAgentFilters, parseDraft, parseDraftInput, parseExecuteResult, parseFilter, parseQuote, parseQuoteOptions, parseReceipt, parseRecipientFilter, safeParseFilter, safeParseRecipientFilter, confirmDeposit as sendConfirmDeposit, createDraft as sendCreateDraft, createQuote as sendCreateQuote, executeSend as sendExecuteSend, getEstimatedTimeRemaining as sendGetEstimatedTimeRemaining, getFailedTransactions as sendGetFailedTransactions, getQuote as sendGetQuote, getReceipt as sendGetReceipt, getSuccessRate as sendGetSuccessRate, isComplete as sendIsComplete, isDepositSufficient as sendIsDepositSufficient, isExecuting as sendIsExecuting, isQuoteExpired as sendIsQuoteExpired, isReadyForDeposit as sendIsReadyForDeposit, isReadyForQuote as sendIsReadyForQuote, isSuccess as sendIsSuccess, pollUntilComplete as sendPollUntilComplete, updateDraft as sendUpdateDraft, validateDraftInput2 as sendValidateDraftInput, validateDraft, validateDraftInput, validateExecuteResult, validateFilter, validateFilterHasTargeting, validateQuote, validateQuoteOptions, validateReceipt, validateRecipientFilter };
3238
3837
  //# sourceMappingURL=index.js.map
3239
3838
  //# sourceMappingURL=index.js.map