@beeperbot/sdk 0.2.2 → 0.2.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
@@ -710,6 +710,48 @@ var FILTER_SCHEMA = [
710
710
  }
711
711
  ]
712
712
  },
713
+ {
714
+ name: "Cast Engagement",
715
+ description: "Target users who engaged with a specific Farcaster cast",
716
+ filters: [
717
+ {
718
+ name: "castEngagement",
719
+ description: "Users who liked and/or recasted a specific cast. If require is omitted, any engagement counts.",
720
+ type: "object",
721
+ example: {
722
+ castUrl: "https://warpcast.com/username/0xabc123",
723
+ require: ["like", "recast"]
724
+ },
725
+ properties: {
726
+ castUrl: {
727
+ name: "castUrl",
728
+ description: "Farcaster cast URL (warpcast.com, farcaster.xyz, base.app)",
729
+ type: "string",
730
+ example: "https://warpcast.com/username/0xabc123"
731
+ },
732
+ castHash: {
733
+ name: "castHash",
734
+ description: "Cast hash (0x...) if you already have it",
735
+ type: "string",
736
+ example: "0xabc123"
737
+ },
738
+ require: {
739
+ name: "require",
740
+ description: "Required engagement types. Use ['like','recast'] to require both.",
741
+ type: "array",
742
+ items: {
743
+ name: "engagement",
744
+ description: "Engagement type",
745
+ type: "string",
746
+ enum: ["like", "recast"],
747
+ example: "like"
748
+ },
749
+ example: ["like", "recast"]
750
+ }
751
+ }
752
+ }
753
+ ]
754
+ },
713
755
  {
714
756
  name: "Token Filters",
715
757
  description: "Filter by token preferences or holdings. IMPORTANT: Signal tokens and token holders are different concepts.",
@@ -1224,6 +1266,541 @@ var AgentClient = class _AgentClient {
1224
1266
  }
1225
1267
  };
1226
1268
 
1269
+ // src/core/agent/errors.ts
1270
+ var AgentCoreError = class _AgentCoreError extends Error {
1271
+ code;
1272
+ constructor(code, message) {
1273
+ super(message);
1274
+ this.name = "AgentCoreError";
1275
+ this.code = code;
1276
+ }
1277
+ static validation(message) {
1278
+ return new _AgentCoreError("VALIDATION_ERROR", message);
1279
+ }
1280
+ static notFound(message) {
1281
+ return new _AgentCoreError("NOT_FOUND", message);
1282
+ }
1283
+ static unsupportedChain(chainId) {
1284
+ return new _AgentCoreError("UNSUPPORTED_CHAIN", `Unsupported chain: ${chainId}`);
1285
+ }
1286
+ static noRecipients(message) {
1287
+ return new _AgentCoreError("NO_RECIPIENTS", message);
1288
+ }
1289
+ };
1290
+
1291
+ // src/core/agent/filters.ts
1292
+ var CHAIN_ID_MAP = {
1293
+ base: 8453,
1294
+ ethereum: 1,
1295
+ mainnet: 1,
1296
+ arbitrum: 42161,
1297
+ polygon: 137,
1298
+ optimism: 10
1299
+ };
1300
+ function hasSubstantiveFilters(filters) {
1301
+ return !!(filters.platform || filters.minFollowers || filters.maxFollowers || filters.fids?.length || filters.userIds?.length || filters.castEngagement?.castHash || filters.castEngagement?.castUrl || filters.activeInLastDays || filters.countries?.length || filters.neynarScoreMin || filters.tokenHolders?.length || filters.maxAttentionPriceUsd || filters.hasBaseWallet || filters.spamLabel);
1302
+ }
1303
+ function normalizeAgentFilters(filters) {
1304
+ const normalized = { ...filters };
1305
+ const defaultsApplied = [];
1306
+ const stripEmptyArray = (key) => {
1307
+ const value = normalized[key];
1308
+ if (Array.isArray(value) && value.length === 0) {
1309
+ delete normalized[key];
1310
+ }
1311
+ };
1312
+ const stripZero = (key) => {
1313
+ const value = normalized[key];
1314
+ if (typeof value === "number" && value === 0) {
1315
+ delete normalized[key];
1316
+ }
1317
+ };
1318
+ if (normalized.platform === "all") delete normalized.platform;
1319
+ if (normalized.spamLabel === "all") delete normalized.spamLabel;
1320
+ stripZero("minFollowers");
1321
+ stripZero("maxFollowers");
1322
+ stripZero("activeInLastDays");
1323
+ stripZero("minBatteryPercentage");
1324
+ stripZero("hasRechargedInLastDays");
1325
+ stripZero("maxAttentionPriceUsd");
1326
+ stripEmptyArray("signalTokens");
1327
+ if (Array.isArray(normalized.tokenHolders)) {
1328
+ normalized.tokenHolders = normalized.tokenHolders.map((holder) => {
1329
+ if (!holder) return null;
1330
+ if ("tokenAddress" in holder && "chainId" in holder) return holder;
1331
+ const loose = holder;
1332
+ if (!loose.contractAddress || loose.chain == null) return null;
1333
+ const chainId = typeof loose.chain === "number" ? loose.chain : CHAIN_ID_MAP[String(loose.chain).toLowerCase()] ?? Number(loose.chain);
1334
+ if (!Number.isFinite(chainId)) return null;
1335
+ return {
1336
+ tokenAddress: loose.contractAddress,
1337
+ chainId,
1338
+ ...loose.minBalance ? { minBalance: loose.minBalance } : {}
1339
+ };
1340
+ }).filter((holder) => !!holder);
1341
+ }
1342
+ stripEmptyArray("tokenHolders");
1343
+ if (Array.isArray(normalized.countries)) {
1344
+ normalized.countries = normalized.countries.map((country) => {
1345
+ if (typeof country === "string") return country;
1346
+ return country?.code;
1347
+ }).filter((country) => typeof country === "string" && country.length > 0).map((country) => country.toUpperCase());
1348
+ }
1349
+ stripEmptyArray("countries");
1350
+ stripEmptyArray("fids");
1351
+ stripEmptyArray("userIds");
1352
+ if (normalized.castEngagement) {
1353
+ const { castHash, castUrl, require: require2 } = normalized.castEngagement;
1354
+ if (!castHash && !castUrl) {
1355
+ delete normalized.castEngagement;
1356
+ } else if (Array.isArray(require2) && require2.length === 0) {
1357
+ normalized.castEngagement = {
1358
+ ...castHash != null && { castHash },
1359
+ ...castUrl != null && { castUrl }
1360
+ };
1361
+ }
1362
+ }
1363
+ if (normalized.neynarScoreMin === 0) delete normalized.neynarScoreMin;
1364
+ if (normalized.neynarScoreMax === 1) delete normalized.neynarScoreMax;
1365
+ if (normalized.quotientScoreMin === 0) delete normalized.quotientScoreMin;
1366
+ if (normalized.quotientScoreMax === 1) delete normalized.quotientScoreMax;
1367
+ if (!hasSubstantiveFilters(normalized)) {
1368
+ normalized.activeInLastDays = 30;
1369
+ normalized.spamLabel = "not_spam_only";
1370
+ defaultsApplied.push("activeInLastDays=30", "spamLabel=not_spam_only");
1371
+ }
1372
+ return { filters: normalized, defaultsApplied };
1373
+ }
1374
+ function getOrderByOrDefault(filters) {
1375
+ return filters.orderBy ?? "attention_price_asc";
1376
+ }
1377
+ function buildAgentQueryPlan(filters) {
1378
+ const normalized = normalizeAgentFilters(filters);
1379
+ return {
1380
+ inputFilters: { ...filters },
1381
+ filters: normalized.filters,
1382
+ orderBy: getOrderByOrDefault(normalized.filters),
1383
+ defaultsApplied: normalized.defaultsApplied
1384
+ };
1385
+ }
1386
+
1387
+ // src/core/agent/costing.ts
1388
+ var DEFAULT_MICRO_USD_PER_USD = 1e6;
1389
+ var DEFAULT_PLATFORM_FEE_RATE = 0.1;
1390
+ var GOAL_BASELINES = {
1391
+ max_reach: { payout: 100, bonus: 0 },
1392
+ lil_mission: { payout: 80, bonus: 20 },
1393
+ hard_mission: { payout: 50, bonus: 50 }
1394
+ };
1395
+ var SPLIT_ADJUSTMENTS = {
1396
+ balanced: 0,
1397
+ more_reach: 10,
1398
+ more_action: -10
1399
+ };
1400
+ function computeBudgetSplit(totalUsd, goalType = "max_reach", splitPreset = "balanced", platformFeeRate = DEFAULT_PLATFORM_FEE_RATE) {
1401
+ const baseline = GOAL_BASELINES[goalType];
1402
+ const adjustment = SPLIT_ADJUSTMENTS[splitPreset];
1403
+ let payoutPercent = Math.max(0, Math.min(100, baseline.payout + adjustment));
1404
+ let bonusPercent = Math.max(0, Math.min(100, baseline.bonus - adjustment));
1405
+ const sum = payoutPercent + bonusPercent;
1406
+ if (sum !== 100) {
1407
+ payoutPercent = payoutPercent / sum * 100;
1408
+ bonusPercent = bonusPercent / sum * 100;
1409
+ }
1410
+ const payoutUsd = roundToCents(totalUsd * payoutPercent / 100);
1411
+ const bonusUsd = roundToCents(totalUsd * bonusPercent / 100);
1412
+ const platformFeeUsd = roundToCents(totalUsd * platformFeeRate);
1413
+ const userPaysUsd = roundToCents(totalUsd + platformFeeUsd);
1414
+ return {
1415
+ totalUsd,
1416
+ payoutUsd,
1417
+ bonusUsd,
1418
+ platformFeeUsd,
1419
+ userPaysUsd,
1420
+ goalType,
1421
+ splitPreset,
1422
+ payoutPercent: Math.round(payoutPercent),
1423
+ bonusPercent: Math.round(bonusPercent)
1424
+ };
1425
+ }
1426
+ function roundToCents(value) {
1427
+ return Math.round(value * 100) / 100;
1428
+ }
1429
+ function parseUsd(value) {
1430
+ return parseFloat(value.replace(/^\$/, "").trim());
1431
+ }
1432
+ function usdToMicroUsd(usd, microUsdPerUsd = DEFAULT_MICRO_USD_PER_USD) {
1433
+ return Math.round(usd * microUsdPerUsd);
1434
+ }
1435
+
1436
+ // src/core/agent/engine.ts
1437
+ var DEFAULT_CHAIN_INFO = {
1438
+ 8453: { name: "Base", usdcAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" }
1439
+ };
1440
+ var AgentCore = class {
1441
+ adapter;
1442
+ defaultChainId;
1443
+ platformFeeRate;
1444
+ estimateMaxUsers;
1445
+ bulkMaxUsers;
1446
+ microUsdPerUsd;
1447
+ chainInfo;
1448
+ constructor(adapter, config = {}) {
1449
+ this.adapter = adapter;
1450
+ this.defaultChainId = config.defaultChainId ?? 8453;
1451
+ this.platformFeeRate = config.platformFeeRate ?? DEFAULT_PLATFORM_FEE_RATE;
1452
+ this.estimateMaxUsers = config.estimateMaxUsers ?? 1e4;
1453
+ this.bulkMaxUsers = config.bulkMaxUsers ?? 1e4;
1454
+ this.microUsdPerUsd = config.microUsdPerUsd ?? DEFAULT_MICRO_USD_PER_USD;
1455
+ this.chainInfo = config.chainInfo ?? DEFAULT_CHAIN_INFO;
1456
+ }
1457
+ buildQueryPlan(filters) {
1458
+ return buildAgentQueryPlan(filters);
1459
+ }
1460
+ normalizeFilters(filters) {
1461
+ return normalizeAgentFilters(filters);
1462
+ }
1463
+ computeBudgetSplit(totalUsd, goalType = "max_reach", splitPreset = "balanced") {
1464
+ return computeBudgetSplit(totalUsd, goalType, splitPreset, this.platformFeeRate);
1465
+ }
1466
+ async lookup(query) {
1467
+ const user = await this.adapter.resolveUser(query);
1468
+ if (!user) return null;
1469
+ const fid = user.fid ?? await this.adapter.getFidForUser(user.id) ?? void 0;
1470
+ const walletAddress = user.walletAddress ?? await this.adapter.getPrimaryWalletAddress(user.id) ?? void 0;
1471
+ return {
1472
+ id: user.id,
1473
+ username: user.username,
1474
+ platform: user.platform,
1475
+ attentionPriceUsd: (Number(user.attentionPriceMicroUsd) / this.microUsdPerUsd).toFixed(2),
1476
+ ...fid !== void 0 ? { fid } : {},
1477
+ ...user.displayName ? { displayName: user.displayName } : {},
1478
+ ...user.pfpUrl ? { pfpUrl: user.pfpUrl } : {},
1479
+ ...walletAddress ? { walletAddress } : {},
1480
+ ...user.followerCount !== void 0 ? { followerCount: user.followerCount } : {}
1481
+ };
1482
+ }
1483
+ async getPrice(query) {
1484
+ const user = await this.lookup(query);
1485
+ if (!user) return null;
1486
+ const priceUsdNum = parseFloat(user.attentionPriceUsd);
1487
+ return {
1488
+ userId: user.id,
1489
+ username: user.username,
1490
+ priceUsd: user.attentionPriceUsd,
1491
+ priceUsdc: usdToMicroUsd(priceUsdNum, this.microUsdPerUsd).toString()
1492
+ };
1493
+ }
1494
+ async prepareBeep(params) {
1495
+ const user = await this.lookup(params.to);
1496
+ if (!user) {
1497
+ return { error: "user_not_found", message: `User not found: ${params.to}` };
1498
+ }
1499
+ if (!user.walletAddress) {
1500
+ return { error: "no_wallet", message: `@${user.username} has no connected wallet` };
1501
+ }
1502
+ const baseAmount = parseUsd(params.amountUsd);
1503
+ if (Number.isNaN(baseAmount) || baseAmount <= 0) {
1504
+ return { error: "invalid_amount", message: "Amount must be a positive number" };
1505
+ }
1506
+ const attentionPrice = parseFloat(user.attentionPriceUsd);
1507
+ if (baseAmount < attentionPrice) {
1508
+ return {
1509
+ error: "below_minimum",
1510
+ message: `Amount $${baseAmount.toFixed(2)} is below @${user.username}'s minimum beep price of $${attentionPrice.toFixed(2)}.`,
1511
+ minimumUsd: user.attentionPriceUsd,
1512
+ requestedUsd: params.amountUsd
1513
+ };
1514
+ }
1515
+ const chainId = params.chainId ?? this.defaultChainId;
1516
+ const chain = this.chainInfo[chainId];
1517
+ if (!chain) {
1518
+ return { error: "unsupported_chain", message: `Chain ${chainId} is not supported` };
1519
+ }
1520
+ const goalType = params.goalType ?? (params.bonusConfig ? "lil_mission" : "max_reach");
1521
+ const hasMissions = goalType !== "max_reach";
1522
+ const splitPreset = params.splitPreset ?? "balanced";
1523
+ const split = hasMissions ? this.computeBudgetSplit(baseAmount, goalType, splitPreset) : void 0;
1524
+ const platformFeeUsd = Math.round(baseAmount * this.platformFeeRate * 100) / 100;
1525
+ const totalAmountUsd = baseAmount + platformFeeUsd;
1526
+ const result = {
1527
+ type: "confirmation",
1528
+ recipient: {
1529
+ username: user.username,
1530
+ walletAddress: user.walletAddress,
1531
+ ...user.displayName ? { displayName: user.displayName } : {},
1532
+ ...user.pfpUrl ? { pfpUrl: user.pfpUrl } : {},
1533
+ ...user.fid !== void 0 ? { fid: user.fid } : {}
1534
+ },
1535
+ message: params.message,
1536
+ baseAmountUsd: baseAmount.toFixed(2),
1537
+ platformFeeUsd: platformFeeUsd.toFixed(2),
1538
+ totalAmountUsd: totalAmountUsd.toFixed(2),
1539
+ feePercentage: this.platformFeeRate * 100,
1540
+ chainId,
1541
+ chainName: chain.name,
1542
+ attentionPriceUsd: user.attentionPriceUsd
1543
+ };
1544
+ if (hasMissions && split) {
1545
+ result.hasMissions = true;
1546
+ result.budgetSplit = split;
1547
+ if (params.bonusConfig) {
1548
+ result.bonusConfig = params.bonusConfig;
1549
+ }
1550
+ }
1551
+ return result;
1552
+ }
1553
+ async createIntent(params) {
1554
+ const user = await this.lookup(params.to);
1555
+ if (!user || !user.walletAddress) return null;
1556
+ const baseAmountUsd = parseUsd(params.amountUsd);
1557
+ if (Number.isNaN(baseAmountUsd) || baseAmountUsd <= 0) {
1558
+ throw AgentCoreError.validation("Invalid amount");
1559
+ }
1560
+ const chainId = params.chainId ?? this.defaultChainId;
1561
+ const chain = this.chainInfo[chainId];
1562
+ if (!chain) throw AgentCoreError.unsupportedChain(chainId);
1563
+ const platformFeeUsd = Math.round(baseAmountUsd * this.platformFeeRate * 100) / 100;
1564
+ const totalAmountUsd = baseAmountUsd + platformFeeUsd;
1565
+ const amountUsdc = usdToMicroUsd(totalAmountUsd, this.microUsdPerUsd).toString();
1566
+ const intentId = `intent_${crypto.randomUUID()}`;
1567
+ const shortAddress = `${user.walletAddress.slice(0, 6)}...${user.walletAddress.slice(-4)}`;
1568
+ const message = params.message ?? "";
1569
+ return {
1570
+ intentId,
1571
+ recipient: user.walletAddress,
1572
+ amount: amountUsdc,
1573
+ amountFormatted: `${totalAmountUsd.toFixed(2)} USDC`,
1574
+ tokenAddress: chain.usdcAddress,
1575
+ tokenSymbol: "USDC",
1576
+ chainId,
1577
+ chainName: chain.name,
1578
+ message,
1579
+ recipientInfo: {
1580
+ username: user.username,
1581
+ ...user.displayName ? { displayName: user.displayName } : {},
1582
+ ...user.fid !== void 0 ? { fid: user.fid } : {}
1583
+ },
1584
+ instruction: `Send ${totalAmountUsd.toFixed(2)} USDC to ${shortAddress} on ${chain.name} (${baseAmountUsd.toFixed(2)} to @${user.username} + ${platformFeeUsd.toFixed(2)} platform fee)`,
1585
+ expiresAt: new Date(Date.now() + 60 * 60 * 1e3).toISOString(),
1586
+ baseAmountUsd: baseAmountUsd.toFixed(2),
1587
+ platformFeeUsd: platformFeeUsd.toFixed(2),
1588
+ totalAmountUsd: totalAmountUsd.toFixed(2),
1589
+ feePercentage: this.platformFeeRate * 100
1590
+ };
1591
+ }
1592
+ async estimate(params) {
1593
+ const budgetUsd = parseUsd(params.budgetUsd);
1594
+ if (Number.isNaN(budgetUsd) || budgetUsd <= 0) {
1595
+ throw AgentCoreError.validation("Budget must be a positive number");
1596
+ }
1597
+ const plan = this.buildQueryPlan(params.filters);
1598
+ const candidates = await this.adapter.findUsers({
1599
+ filters: plan.filters,
1600
+ orderBy: plan.orderBy,
1601
+ limit: this.estimateMaxUsers
1602
+ });
1603
+ const budgetMicro = usdToMicroUsd(budgetUsd, this.microUsdPerUsd);
1604
+ let totalCost = 0;
1605
+ let recipientCount = 0;
1606
+ let minPrice = Number.POSITIVE_INFINITY;
1607
+ let maxPrice = 0;
1608
+ for (const candidate of candidates) {
1609
+ const priceMicro = Number(candidate.attentionPriceMicroUsd);
1610
+ if (totalCost + priceMicro > budgetMicro) break;
1611
+ totalCost += priceMicro;
1612
+ recipientCount += 1;
1613
+ if (priceMicro < minPrice) minPrice = priceMicro;
1614
+ if (priceMicro > maxPrice) maxPrice = priceMicro;
1615
+ }
1616
+ const avgPrice = recipientCount > 0 ? totalCost / recipientCount : 0;
1617
+ return {
1618
+ recipientCount,
1619
+ totalCostUsd: (totalCost / this.microUsdPerUsd).toFixed(2),
1620
+ avgPriceUsd: (avgPrice / this.microUsdPerUsd).toFixed(2),
1621
+ minPriceUsd: recipientCount > 0 ? (minPrice / this.microUsdPerUsd).toFixed(2) : "0.00",
1622
+ maxPriceUsd: recipientCount > 0 ? (maxPrice / this.microUsdPerUsd).toFixed(2) : "0.00",
1623
+ remainingBudgetUsd: ((budgetMicro - totalCost) / this.microUsdPerUsd).toFixed(2),
1624
+ budgetSufficient: recipientCount > 0,
1625
+ diagnostics: {
1626
+ candidateCount: candidates.length,
1627
+ budgetMicro,
1628
+ appliedDefaults: plan.defaultsApplied
1629
+ }
1630
+ };
1631
+ }
1632
+ async preview(params) {
1633
+ const limit = Math.min(Math.max(params.limit, 1), 20);
1634
+ const plan = this.buildQueryPlan(params.filters);
1635
+ const [users, totalCount] = await Promise.all([
1636
+ this.adapter.findUsers({
1637
+ filters: plan.filters,
1638
+ orderBy: plan.orderBy,
1639
+ limit,
1640
+ includeProfile: true,
1641
+ includeReputation: true
1642
+ }),
1643
+ this.adapter.countUsers(plan.filters)
1644
+ ]);
1645
+ const userIds = users.map((user) => user.id);
1646
+ const fidMap = await this.adapter.getBulkFidsForUsers(userIds);
1647
+ return {
1648
+ users: users.map((user) => {
1649
+ const previewUser = {
1650
+ username: user.username,
1651
+ priceUsd: (Number(user.attentionPriceMicroUsd) / this.microUsdPerUsd).toFixed(2),
1652
+ ...user.displayName ? { displayName: user.displayName } : {},
1653
+ ...user.followerCount !== void 0 ? { followerCount: user.followerCount } : {},
1654
+ ...user.neynarScore !== void 0 ? { neynarScore: user.neynarScore } : {},
1655
+ ...user.pfpUrl ? { pfpUrl: user.pfpUrl } : {}
1656
+ };
1657
+ const fid = fidMap.get(user.id);
1658
+ if (fid !== void 0) {
1659
+ return { ...previewUser, fid };
1660
+ }
1661
+ return previewUser;
1662
+ }),
1663
+ totalCount,
1664
+ hasMore: totalCount > limit
1665
+ };
1666
+ }
1667
+ async createBulkIntent(params) {
1668
+ const budgetUsd = parseUsd(params.budgetUsd);
1669
+ if (Number.isNaN(budgetUsd) || budgetUsd <= 0) {
1670
+ throw AgentCoreError.validation("Budget must be a positive number");
1671
+ }
1672
+ const chainId = params.chainId ?? this.defaultChainId;
1673
+ const chain = this.chainInfo[chainId];
1674
+ if (!chain) throw AgentCoreError.unsupportedChain(chainId);
1675
+ const plan = this.buildQueryPlan(params.filters);
1676
+ const users = await this.adapter.findUsers({
1677
+ filters: plan.filters,
1678
+ orderBy: "attention_price_asc",
1679
+ limit: this.bulkMaxUsers
1680
+ });
1681
+ const budgetMicro = usdToMicroUsd(budgetUsd, this.microUsdPerUsd);
1682
+ let totalAmountMicro = 0;
1683
+ const selectedUsers = [];
1684
+ for (const user of users) {
1685
+ const priceMicro = Number(user.attentionPriceMicroUsd);
1686
+ if (totalAmountMicro + priceMicro > budgetMicro) break;
1687
+ totalAmountMicro += priceMicro;
1688
+ selectedUsers.push({
1689
+ id: user.id,
1690
+ username: user.username,
1691
+ amountMicro: priceMicro
1692
+ });
1693
+ }
1694
+ if (selectedUsers.length === 0) {
1695
+ throw AgentCoreError.noRecipients("No eligible recipients within budget");
1696
+ }
1697
+ const walletMap = await this.adapter.getBulkPrimaryWalletAddresses(selectedUsers.map((u) => u.id));
1698
+ const payments = selectedUsers.filter((user) => walletMap.has(user.id)).map((user) => ({
1699
+ recipient: walletMap.get(user.id),
1700
+ amount: user.amountMicro.toString(),
1701
+ username: user.username
1702
+ }));
1703
+ if (payments.length === 0) {
1704
+ throw AgentCoreError.noRecipients("No recipients with valid wallets found");
1705
+ }
1706
+ const totalAmount = payments.reduce((sum, payment) => sum + BigInt(payment.amount), 0n);
1707
+ const totalFormatted = (Number(totalAmount) / this.microUsdPerUsd).toFixed(2);
1708
+ return {
1709
+ bulkIntentId: `bulk_${crypto.randomUUID()}`,
1710
+ recipientCount: payments.length,
1711
+ totalAmount: totalAmount.toString(),
1712
+ totalAmountFormatted: `${totalFormatted} USDC`,
1713
+ payments,
1714
+ summary: `Send to ${payments.length} recipients for total ${totalFormatted} USDC on ${chain.name}`,
1715
+ chainId,
1716
+ chainName: chain.name,
1717
+ tokenAddress: chain.usdcAddress,
1718
+ tokenSymbol: "USDC",
1719
+ expiresAt: new Date(Date.now() + 60 * 60 * 1e3).toISOString()
1720
+ };
1721
+ }
1722
+ async prepareCampaign(params) {
1723
+ const totalBudget = parseUsd(params.budgetUsd);
1724
+ if (Number.isNaN(totalBudget) || totalBudget <= 0) {
1725
+ throw AgentCoreError.validation("Budget must be a positive number");
1726
+ }
1727
+ const chainId = params.chainId ?? this.defaultChainId;
1728
+ const chain = this.chainInfo[chainId];
1729
+ if (!chain) throw AgentCoreError.unsupportedChain(chainId);
1730
+ const goalType = params.goalType ?? (params.bonusConfig ? "lil_mission" : "max_reach");
1731
+ const splitPreset = params.splitPreset ?? "balanced";
1732
+ const split = this.computeBudgetSplit(totalBudget, goalType, splitPreset);
1733
+ const payoutEstimate = await this.estimate({
1734
+ filters: params.filters,
1735
+ budgetUsd: split.payoutUsd.toFixed(2),
1736
+ ...params.message ? { message: params.message } : {}
1737
+ });
1738
+ let missionSummary;
1739
+ if (params.bonusConfig) {
1740
+ const ctaLabels = params.bonusConfig.ctas.map((cta) => {
1741
+ switch (cta.type) {
1742
+ case "follow_profile":
1743
+ return `Follow @${cta.profileUsername || cta.profileFid}`;
1744
+ case "like_cast":
1745
+ return "Like a cast";
1746
+ case "recast":
1747
+ return "Recast";
1748
+ case "share_cast":
1749
+ return "Quote cast";
1750
+ case "visit_link":
1751
+ return `Visit ${cta.label || cta.url || "link"}`;
1752
+ case "quiz":
1753
+ return `Quiz (${cta.questions?.length || 0} questions)`;
1754
+ case "external_verify":
1755
+ return cta.description || "Custom quest";
1756
+ case "x_follow":
1757
+ return `Follow @${cta.handle} on X`;
1758
+ case "x_like":
1759
+ return "Like tweet on X";
1760
+ case "x_recast":
1761
+ return "Repost on X";
1762
+ default:
1763
+ return cta.type;
1764
+ }
1765
+ });
1766
+ const distributionLabel = params.bonusConfig.type === "lottery" ? "BEEPERY (lottery)" : "FCFS (first come)";
1767
+ missionSummary = `${distributionLabel} \u2022 $${split.bonusUsd.toFixed(2)} pool \u2022 ${ctaLabels.join(", ")}`;
1768
+ }
1769
+ return {
1770
+ type: "campaign_confirmation",
1771
+ recipientCount: payoutEstimate.recipientCount,
1772
+ budgetSplit: split,
1773
+ payoutEstimate: {
1774
+ recipientCount: payoutEstimate.recipientCount,
1775
+ totalPayoutCostUsd: payoutEstimate.totalCostUsd,
1776
+ avgPriceUsd: payoutEstimate.avgPriceUsd
1777
+ },
1778
+ platformFeeUsd: split.platformFeeUsd.toFixed(2),
1779
+ userPaysUsd: split.userPaysUsd.toFixed(2),
1780
+ chainId,
1781
+ chainName: chain.name,
1782
+ ...params.bonusConfig ? { bonusConfig: params.bonusConfig } : {},
1783
+ ...missionSummary ? { missionSummary } : {},
1784
+ ...params.message ? { message: params.message } : {}
1785
+ };
1786
+ }
1787
+ async getBeepHistory(params) {
1788
+ return this.adapter.getBeepHistory(params);
1789
+ }
1790
+ };
1791
+ function createAgentCore(adapter, config) {
1792
+ return new AgentCore(adapter, config);
1793
+ }
1794
+ function normalizeFilters(filters) {
1795
+ return normalizeAgentFilters(filters);
1796
+ }
1797
+ function buildQueryPlan(filters) {
1798
+ return buildAgentQueryPlan(filters);
1799
+ }
1800
+ function getDefaultOrderBy(filters) {
1801
+ return buildAgentQueryPlan(filters).orderBy;
1802
+ }
1803
+
1227
1804
  // src/client/auth.ts
1228
1805
  var API_KEY_PREFIXES = {
1229
1806
  LIVE: "bpk_live_",
@@ -3317,6 +3894,8 @@ exports.API_BASE_URLS = API_BASE_URLS;
3317
3894
  exports.API_KEY_PREFIXES = API_KEY_PREFIXES;
3318
3895
  exports.ActiveInLastDaysFilterSchema = ActiveInLastDaysFilterSchema;
3319
3896
  exports.AgentClient = AgentClient;
3897
+ exports.AgentCore = AgentCore;
3898
+ exports.AgentCoreError = AgentCoreError;
3320
3899
  exports.ApiQuoteResponseSchema = ApiQuoteResponseSchema;
3321
3900
  exports.ApiReceiptResponseSchema = ApiReceiptResponseSchema;
3322
3901
  exports.BeeperClient = BeeperClient;
@@ -3410,6 +3989,10 @@ exports.TokenHolderFilterSchema = TokenHolderFilterSchema;
3410
3989
  exports.TokenTypeSchema = TokenTypeSchema;
3411
3990
  exports.TransferStatusSchema = TransferStatusSchema;
3412
3991
  exports.VerifiedOnlyFilterSchema = VerifiedOnlyFilterSchema;
3992
+ exports.buildAgentCoreQueryPlan = buildQueryPlan;
3993
+ exports.buildAgentQueryPlan = buildAgentQueryPlan;
3994
+ exports.computeAgentBudgetSplit = computeBudgetSplit;
3995
+ exports.createAgentCore = createAgentCore;
3413
3996
  exports.createHttpConfig = createHttpConfig;
3414
3997
  exports.default = BeeperClient_default;
3415
3998
  exports.describeFilters = describeFilters;
@@ -3417,12 +4000,16 @@ exports.generateDepositIdempotencyKey = generateDepositIdempotencyKey;
3417
4000
  exports.generateExecuteIdempotencyKey = generateExecuteIdempotencyKey;
3418
4001
  exports.generateFilterDocumentation = generateFilterDocumentation;
3419
4002
  exports.generateIdempotencyKey = generateIdempotencyKey;
4003
+ exports.getAgentCoreDefaultOrderBy = getDefaultOrderBy;
3420
4004
  exports.getAllFilterNames = getAllFilterNames;
3421
4005
  exports.getApiKeyEnvironment = getApiKeyEnvironment;
3422
4006
  exports.getFilterSchema = getFilterSchema;
4007
+ exports.getOrderByOrDefault = getOrderByOrDefault;
3423
4008
  exports.isRetryableCode = isRetryableCode;
3424
4009
  exports.isValidApiKeyFormat = isValidApiKeyFormat;
3425
4010
  exports.maskApiKey = maskApiKey;
4011
+ exports.normalizeAgentCoreFilters = normalizeFilters;
4012
+ exports.normalizeAgentFilters = normalizeAgentFilters;
3426
4013
  exports.parseDraft = parseDraft;
3427
4014
  exports.parseDraftInput = parseDraftInput;
3428
4015
  exports.parseExecuteResult = parseExecuteResult;