@virtuals-protocol/acp-node 0.3.0-beta.37 → 0.3.0-beta.39

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
@@ -8,7 +8,7 @@ var require_package = __commonJS({
8
8
  "package.json"(exports, module) {
9
9
  module.exports = {
10
10
  name: "@virtuals-protocol/acp-node",
11
- version: "0.3.0-beta.37",
11
+ version: "0.3.0-beta.39",
12
12
  main: "./dist/index.js",
13
13
  module: "./dist/index.mjs",
14
14
  types: "./dist/index.d.ts",
@@ -43,6 +43,7 @@ var require_package = __commonJS({
43
43
  ajv: "^8.17.1",
44
44
  axios: "^1.13.2",
45
45
  "jwt-decode": "^4.0.0",
46
+ ox: "^0.13.1",
46
47
  "socket.io-client": "^4.8.1",
47
48
  tsup: "^8.5.0",
48
49
  viem: "^2.28.2"
@@ -1345,7 +1346,7 @@ var FareAmount = class extends FareAmountBase {
1345
1346
  super(fare.formatAmount(truncateTo6Decimals(fareAmount.toString())), fare);
1346
1347
  }
1347
1348
  add(other) {
1348
- if (this.fare.contractAddress !== other.fare.contractAddress) {
1349
+ if (this.fare.contractAddress.toLowerCase() !== other.fare.contractAddress.toLowerCase()) {
1349
1350
  throw new Error("Token addresses do not match");
1350
1351
  }
1351
1352
  return new FareBigInt(this.amount + other.amount, this.fare);
@@ -1378,6 +1379,10 @@ var ACP_V2_ABI = [
1378
1379
  name: "AccessControlUnauthorizedAccount",
1379
1380
  type: "error"
1380
1381
  },
1382
+ { inputs: [], name: "AccountAlreadySubscribed", type: "error" },
1383
+ { inputs: [], name: "AccountDoesNotExist", type: "error" },
1384
+ { inputs: [], name: "AccountManagerNotSet", type: "error" },
1385
+ { inputs: [], name: "AccountNotActive", type: "error" },
1381
1386
  {
1382
1387
  inputs: [{ internalType: "address", name: "target", type: "address" }],
1383
1388
  name: "AddressEmptyCode",
@@ -1388,6 +1393,13 @@ var ACP_V2_ABI = [
1388
1393
  name: "AddressInsufficientBalance",
1389
1394
  type: "error"
1390
1395
  },
1396
+ { inputs: [], name: "AmountMustBeGreaterThanZero", type: "error" },
1397
+ { inputs: [], name: "AmountOrFeeRequired", type: "error" },
1398
+ { inputs: [], name: "AssetManagerNotSet", type: "error" },
1399
+ { inputs: [], name: "BudgetNotReceived", type: "error" },
1400
+ { inputs: [], name: "CannotSetBudgetOnSubscriptionJob", type: "error" },
1401
+ { inputs: [], name: "DestinationEndpointRequired", type: "error" },
1402
+ { inputs: [], name: "DurationMustBeGreaterThanZero", type: "error" },
1391
1403
  {
1392
1404
  inputs: [
1393
1405
  { internalType: "address", name: "implementation", type: "address" }
@@ -1397,22 +1409,46 @@ var ACP_V2_ABI = [
1397
1409
  },
1398
1410
  { inputs: [], name: "ERC1967NonPayable", type: "error" },
1399
1411
  { inputs: [], name: "EnforcedPause", type: "error" },
1412
+ { inputs: [], name: "EvaluatorFeeTooHigh", type: "error" },
1400
1413
  { inputs: [], name: "ExpectedPause", type: "error" },
1414
+ { inputs: [], name: "ExpiredAtMustBeInFuture", type: "error" },
1415
+ { inputs: [], name: "ExpiryTooShort", type: "error" },
1401
1416
  { inputs: [], name: "FailedInnerCall", type: "error" },
1417
+ { inputs: [], name: "InvalidCrossChainMemoType", type: "error" },
1402
1418
  { inputs: [], name: "InvalidInitialization", type: "error" },
1419
+ { inputs: [], name: "InvalidMemoType", type: "error" },
1420
+ { inputs: [], name: "InvalidModuleType", type: "error" },
1421
+ { inputs: [], name: "InvalidPaymentToken", type: "error" },
1422
+ { inputs: [], name: "InvalidRecipient", type: "error" },
1423
+ { inputs: [], name: "JobManagerNotSet", type: "error" },
1424
+ { inputs: [], name: "MemoManagerNotSet", type: "error" },
1403
1425
  { inputs: [], name: "NotInitializing", type: "error" },
1426
+ { inputs: [], name: "OnlyClient", type: "error" },
1427
+ { inputs: [], name: "OnlyMemoManager", type: "error" },
1428
+ { inputs: [], name: "OnlyProvider", type: "error" },
1429
+ { inputs: [], name: "PaymentManagerNotSet", type: "error" },
1430
+ { inputs: [], name: "PlatformFeeTooHigh", type: "error" },
1404
1431
  { inputs: [], name: "ReentrancyGuardReentrantCall", type: "error" },
1405
1432
  {
1406
1433
  inputs: [{ internalType: "address", name: "token", type: "address" }],
1407
1434
  name: "SafeERC20FailedOperation",
1408
1435
  type: "error"
1409
1436
  },
1437
+ { inputs: [], name: "SubscriptionAccountExpired", type: "error" },
1438
+ { inputs: [], name: "SubscriptionJobMustHaveZeroBudget", type: "error" },
1439
+ { inputs: [], name: "SubscriptionJobMustHaveZeroBudgetMemo", type: "error" },
1440
+ { inputs: [], name: "TokenAddressRequired", type: "error" },
1441
+ { inputs: [], name: "TokenMustBeERC20", type: "error" },
1410
1442
  { inputs: [], name: "UUPSUnauthorizedCallContext", type: "error" },
1411
1443
  {
1412
1444
  inputs: [{ internalType: "bytes32", name: "slot", type: "bytes32" }],
1413
1445
  name: "UUPSUnsupportedProxiableUUID",
1414
1446
  type: "error"
1415
1447
  },
1448
+ { inputs: [], name: "UnableToRefund", type: "error" },
1449
+ { inputs: [], name: "Unauthorized", type: "error" },
1450
+ { inputs: [], name: "ZeroAddress", type: "error" },
1451
+ { inputs: [], name: "ZeroAddressProvider", type: "error" },
1416
1452
  {
1417
1453
  anonymous: false,
1418
1454
  inputs: [
@@ -1452,56 +1488,6 @@ var ACP_V2_ABI = [
1452
1488
  name: "AccountStatusUpdated",
1453
1489
  type: "event"
1454
1490
  },
1455
- {
1456
- anonymous: false,
1457
- inputs: [
1458
- {
1459
- indexed: false,
1460
- internalType: "uint256",
1461
- name: "jobId",
1462
- type: "uint256"
1463
- },
1464
- {
1465
- indexed: true,
1466
- internalType: "address",
1467
- name: "evaluator",
1468
- type: "address"
1469
- },
1470
- {
1471
- indexed: false,
1472
- internalType: "uint256",
1473
- name: "evaluatorFee",
1474
- type: "uint256"
1475
- }
1476
- ],
1477
- name: "ClaimedEvaluatorFee",
1478
- type: "event"
1479
- },
1480
- {
1481
- anonymous: false,
1482
- inputs: [
1483
- {
1484
- indexed: false,
1485
- internalType: "uint256",
1486
- name: "jobId",
1487
- type: "uint256"
1488
- },
1489
- {
1490
- indexed: true,
1491
- internalType: "address",
1492
- name: "provider",
1493
- type: "address"
1494
- },
1495
- {
1496
- indexed: false,
1497
- internalType: "uint256",
1498
- name: "providerFee",
1499
- type: "uint256"
1500
- }
1501
- ],
1502
- name: "ClaimedProviderFee",
1503
- type: "event"
1504
- },
1505
1491
  {
1506
1492
  anonymous: false,
1507
1493
  inputs: [
@@ -1553,31 +1539,6 @@ var ACP_V2_ABI = [
1553
1539
  name: "Paused",
1554
1540
  type: "event"
1555
1541
  },
1556
- {
1557
- anonymous: false,
1558
- inputs: [
1559
- {
1560
- indexed: false,
1561
- internalType: "uint256",
1562
- name: "jobId",
1563
- type: "uint256"
1564
- },
1565
- {
1566
- indexed: true,
1567
- internalType: "address",
1568
- name: "client",
1569
- type: "address"
1570
- },
1571
- {
1572
- indexed: false,
1573
- internalType: "uint256",
1574
- name: "amount",
1575
- type: "uint256"
1576
- }
1577
- ],
1578
- name: "RefundedBudget",
1579
- type: "event"
1580
- },
1581
1542
  {
1582
1543
  anonymous: false,
1583
1544
  inputs: [
@@ -1725,6 +1686,13 @@ var ACP_V2_ABI = [
1725
1686
  stateMutability: "nonpayable",
1726
1687
  type: "function"
1727
1688
  },
1689
+ {
1690
+ inputs: [{ internalType: "uint256", name: "jobId", type: "uint256" }],
1691
+ name: "claimBudgetFromMemoManager",
1692
+ outputs: [],
1693
+ stateMutability: "nonpayable",
1694
+ type: "function"
1695
+ },
1728
1696
  {
1729
1697
  inputs: [
1730
1698
  { internalType: "address", name: "provider", type: "address" },
@@ -1838,6 +1806,28 @@ var ACP_V2_ABI = [
1838
1806
  stateMutability: "nonpayable",
1839
1807
  type: "function"
1840
1808
  },
1809
+ {
1810
+ inputs: [
1811
+ { internalType: "uint256", name: "jobId", type: "uint256" },
1812
+ { internalType: "string", name: "content", type: "string" },
1813
+ { internalType: "address", name: "token", type: "address" },
1814
+ { internalType: "uint256", name: "amount", type: "uint256" },
1815
+ { internalType: "address", name: "recipient", type: "address" },
1816
+ { internalType: "uint256", name: "feeAmount", type: "uint256" },
1817
+ { internalType: "enum ACPTypes.FeeType", name: "feeType", type: "uint8" },
1818
+ { internalType: "uint256", name: "duration", type: "uint256" },
1819
+ { internalType: "uint256", name: "expiredAt", type: "uint256" },
1820
+ {
1821
+ internalType: "enum ACPTypes.JobPhase",
1822
+ name: "nextPhase",
1823
+ type: "uint8"
1824
+ }
1825
+ ],
1826
+ name: "createSubscriptionMemo",
1827
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
1828
+ stateMutability: "nonpayable",
1829
+ type: "function"
1830
+ },
1841
1831
  {
1842
1832
  inputs: [
1843
1833
  { internalType: "address", name: "provider", type: "address" },
@@ -1906,7 +1896,8 @@ var ACP_V2_ABI = [
1906
1896
  name: "completedJobCount",
1907
1897
  type: "uint256"
1908
1898
  },
1909
- { internalType: "bool", name: "isActive", type: "bool" }
1899
+ { internalType: "bool", name: "isActive", type: "bool" },
1900
+ { internalType: "uint256", name: "expiry", type: "uint256" }
1910
1901
  ],
1911
1902
  internalType: "struct ACPTypes.Account",
1912
1903
  name: "",
@@ -2063,13 +2054,6 @@ var ACP_V2_ABI = [
2063
2054
  stateMutability: "view",
2064
2055
  type: "function"
2065
2056
  },
2066
- {
2067
- inputs: [],
2068
- name: "getPhases",
2069
- outputs: [{ internalType: "string[7]", name: "", type: "string[7]" }],
2070
- stateMutability: "pure",
2071
- type: "function"
2072
- },
2073
2057
  {
2074
2058
  inputs: [{ internalType: "bytes32", name: "role", type: "bytes32" }],
2075
2059
  name: "getRoleAdmin",
@@ -2114,11 +2098,8 @@ var ACP_V2_ABI = [
2114
2098
  type: "function"
2115
2099
  },
2116
2100
  {
2117
- inputs: [
2118
- { internalType: "uint256", name: "jobId", type: "uint256" },
2119
- { internalType: "address", name: "account", type: "address" }
2120
- ],
2121
- name: "isJobEvaluator",
2101
+ inputs: [{ internalType: "uint256", name: "jobId", type: "uint256" }],
2102
+ name: "isSubscriptionJob",
2122
2103
  outputs: [{ internalType: "bool", name: "", type: "bool" }],
2123
2104
  stateMutability: "view",
2124
2105
  type: "function"
@@ -2226,6 +2207,13 @@ var ACP_V2_ABI = [
2226
2207
  stateMutability: "nonpayable",
2227
2208
  type: "function"
2228
2209
  },
2210
+ {
2211
+ inputs: [{ internalType: "uint256", name: "jobId", type: "uint256" }],
2212
+ name: "setupEscrowFromMemoManager",
2213
+ outputs: [],
2214
+ stateMutability: "nonpayable",
2215
+ type: "function"
2216
+ },
2229
2217
  {
2230
2218
  inputs: [
2231
2219
  { internalType: "uint256", name: "memoId", type: "uint256" },
@@ -2261,6 +2249,18 @@ var ACP_V2_ABI = [
2261
2249
  stateMutability: "nonpayable",
2262
2250
  type: "function"
2263
2251
  },
2252
+ {
2253
+ inputs: [
2254
+ { internalType: "uint256", name: "accountId", type: "uint256" },
2255
+ { internalType: "uint256", name: "duration", type: "uint256" },
2256
+ { internalType: "string", name: "metadata", type: "string" },
2257
+ { internalType: "address", name: "provider", type: "address" }
2258
+ ],
2259
+ name: "updateAccountSubscriptionFromMemoManager",
2260
+ outputs: [],
2261
+ stateMutability: "nonpayable",
2262
+ type: "function"
2263
+ },
2264
2264
  {
2265
2265
  inputs: [
2266
2266
  { internalType: "uint256", name: "evaluatorFeeBP_", type: "uint256" }
@@ -3611,6 +3611,7 @@ var MemoType = /* @__PURE__ */ ((MemoType3) => {
3611
3611
  MemoType3[MemoType3["PAYABLE_TRANSFER_ESCROW"] = 8] = "PAYABLE_TRANSFER_ESCROW";
3612
3612
  MemoType3[MemoType3["NOTIFICATION"] = 9] = "NOTIFICATION";
3613
3613
  MemoType3[MemoType3["PAYABLE_NOTIFICATION"] = 10] = "PAYABLE_NOTIFICATION";
3614
+ MemoType3[MemoType3["PAYABLE_REQUEST_SUBSCRIPTION"] = 11] = "PAYABLE_REQUEST_SUBSCRIPTION";
3614
3615
  return MemoType3;
3615
3616
  })(MemoType || {});
3616
3617
  var AcpJobPhases = /* @__PURE__ */ ((AcpJobPhases3) => {
@@ -3679,6 +3680,35 @@ ${JSON.stringify(
3679
3680
  );
3680
3681
  }
3681
3682
  }
3683
+ /**
3684
+ * Returns a createAccount operation payload if the contract supports it (V2).
3685
+ * Returns null for V1 or when the ABI does not include createAccount.
3686
+ */
3687
+ createAccount(providerAddress, metadata) {
3688
+ try {
3689
+ const hasCreateAccount = this.abi.some(
3690
+ (item) => item.type === "function" && item.name === "createAccount"
3691
+ );
3692
+ if (!hasCreateAccount) return null;
3693
+ const data = encodeFunctionData({
3694
+ abi: this.abi,
3695
+ functionName: "createAccount",
3696
+ args: [providerAddress, metadata]
3697
+ });
3698
+ return {
3699
+ data,
3700
+ contractAddress: this.contractAddress
3701
+ };
3702
+ } catch {
3703
+ return null;
3704
+ }
3705
+ }
3706
+ /**
3707
+ * Returns the new account id from a createAccount user op receipt, or null if not supported.
3708
+ */
3709
+ async getAccountIdFromUserOpHash(_userOpHash) {
3710
+ return null;
3711
+ }
3682
3712
  get walletAddress() {
3683
3713
  return this.agentWalletAddress;
3684
3714
  }
@@ -3771,6 +3801,33 @@ ${JSON.stringify(
3771
3801
  throw new acpError_default("Failed to create payable memo", error);
3772
3802
  }
3773
3803
  }
3804
+ createSubscriptionMemo(jobId, content, amountBaseUnit, recipient, feeAmountBaseUnit, feeType, duration, nextPhase, expiredAt, token = this.config.baseFare.contractAddress) {
3805
+ try {
3806
+ const data = encodeFunctionData({
3807
+ abi: this.abi,
3808
+ functionName: "createSubscriptionMemo",
3809
+ args: [
3810
+ jobId,
3811
+ content,
3812
+ token,
3813
+ amountBaseUnit,
3814
+ recipient,
3815
+ feeAmountBaseUnit,
3816
+ feeType,
3817
+ duration,
3818
+ Math.floor(expiredAt.getTime() / 1e3),
3819
+ nextPhase
3820
+ ]
3821
+ });
3822
+ const payload = {
3823
+ data,
3824
+ contractAddress: this.contractAddress
3825
+ };
3826
+ return payload;
3827
+ } catch (error) {
3828
+ throw new acpError_default("Failed to create subscription memo", error);
3829
+ }
3830
+ }
3774
3831
  createCrossChainPayableMemo(jobId, content, token, amountBaseUnit, recipient, feeAmountBaseUnit, feeType, type, expiredAt, nextPhase, destinationEid, secured = true) {
3775
3832
  try {
3776
3833
  const data = encodeFunctionData({
@@ -4005,7 +4062,12 @@ var AcpOnlineStatus = /* @__PURE__ */ ((AcpOnlineStatus2) => {
4005
4062
  })(AcpOnlineStatus || {});
4006
4063
 
4007
4064
  // src/utils.ts
4008
- import { decodeAbiParameters, encodeAbiParameters } from "viem";
4065
+ import {
4066
+ concatHex,
4067
+ decodeAbiParameters,
4068
+ encodeAbiParameters,
4069
+ pad
4070
+ } from "viem";
4009
4071
  import {
4010
4072
  arbitrum as arbitrum3,
4011
4073
  arbitrumSepolia as arbitrumSepolia3,
@@ -4084,12 +4146,48 @@ function getDestinationChainId(endpointId) {
4084
4146
  }
4085
4147
  throw new Error(`Unsupported endpoint ID: ${endpointId}`);
4086
4148
  }
4149
+ function appendBuilderCodeData(data, suffix) {
4150
+ const opDataByteLength = (data.length - 2) / 2;
4151
+ const suffixByteLength = (suffix.length - 2) / 2;
4152
+ const opDataPaddedSize = Math.ceil(opDataByteLength / 32) * 32;
4153
+ const suffixPaddedSize = Math.ceil(suffixByteLength / 32) * 32;
4154
+ const paddedData = pad(data, { size: opDataPaddedSize, dir: "right" });
4155
+ const paddedSuffix = pad(suffix, { size: suffixPaddedSize });
4156
+ return concatHex([paddedData, paddedSuffix]);
4157
+ }
4087
4158
 
4088
4159
  // src/acpJobOffering.ts
4089
4160
  import { zeroAddress as zeroAddress2 } from "viem";
4090
4161
  import Ajv from "ajv";
4162
+
4163
+ // src/acpAccount.ts
4164
+ var AcpAccount = class {
4165
+ constructor(contractClient, id, clientAddress, providerAddress, metadata, expiryAt) {
4166
+ this.contractClient = contractClient;
4167
+ this.id = id;
4168
+ this.clientAddress = clientAddress;
4169
+ this.providerAddress = providerAddress;
4170
+ this.metadata = metadata;
4171
+ this.expiryAt = expiryAt;
4172
+ }
4173
+ async updateMetadata(metadata) {
4174
+ const hash = await this.contractClient.updateAccountMetadata(
4175
+ this.id,
4176
+ JSON.stringify(metadata)
4177
+ );
4178
+ return hash;
4179
+ }
4180
+ };
4181
+
4182
+ // src/acpJobOffering.ts
4183
+ var PriceType = /* @__PURE__ */ ((PriceType2) => {
4184
+ PriceType2["FIXED"] = "fixed";
4185
+ PriceType2["PERCENTAGE"] = "percentage";
4186
+ PriceType2["SUBSCRIPTION"] = "subscription";
4187
+ return PriceType2;
4188
+ })(PriceType || {});
4091
4189
  var AcpJobOffering = class {
4092
- constructor(acpClient, acpContractClient, providerAddress, name, price, priceType, requiredFunds, slaMinutes, requirement, deliverable) {
4190
+ constructor(acpClient, acpContractClient, providerAddress, name, price, priceType = "fixed" /* FIXED */, requiredFunds, slaMinutes, requirement, deliverable, subscriptionTiers = [], isPrivate = false) {
4093
4191
  this.acpClient = acpClient;
4094
4192
  this.acpContractClient = acpContractClient;
4095
4193
  this.providerAddress = providerAddress;
@@ -4100,10 +4198,49 @@ var AcpJobOffering = class {
4100
4198
  this.slaMinutes = slaMinutes;
4101
4199
  this.requirement = requirement;
4102
4200
  this.deliverable = deliverable;
4201
+ this.subscriptionTiers = subscriptionTiers;
4202
+ this.isPrivate = isPrivate;
4103
4203
  this.ajv = new Ajv({ allErrors: true });
4104
4204
  }
4105
- async initiateJob(serviceRequirement, evaluatorAddress) {
4106
- const expiredAt = new Date(Date.now() + this.slaMinutes * 60 * 1e3);
4205
+ async initiateJob(serviceRequirement, evaluatorAddress, expiredAt = new Date(Date.now() + this.slaMinutes * 60 * 1e3), preferredSubscriptionTier) {
4206
+ this.validateRequest(serviceRequirement);
4207
+ const finalServiceRequirement = {
4208
+ name: this.name,
4209
+ requirement: serviceRequirement,
4210
+ priceValue: this.price,
4211
+ priceType: this.priceType
4212
+ };
4213
+ const subscriptionRequired = this.isSubscriptionRequired(
4214
+ preferredSubscriptionTier
4215
+ );
4216
+ this.validateSubscriptionTier(preferredSubscriptionTier);
4217
+ const effectivePrice = subscriptionRequired ? 0 : this.price;
4218
+ const effectivePriceType = subscriptionRequired ? "subscription" /* SUBSCRIPTION */ : this.priceType === "subscription" /* SUBSCRIPTION */ ? "fixed" /* FIXED */ : this.priceType;
4219
+ const fareAmount = new FareAmount(
4220
+ effectivePriceType === "fixed" /* FIXED */ ? effectivePrice : 0,
4221
+ this.acpContractClient.config.baseFare
4222
+ );
4223
+ const account = await this.resolveAccount(
4224
+ subscriptionRequired,
4225
+ preferredSubscriptionTier
4226
+ );
4227
+ const jobId = await this.createJob(
4228
+ account,
4229
+ evaluatorAddress,
4230
+ expiredAt,
4231
+ fareAmount,
4232
+ subscriptionRequired,
4233
+ preferredSubscriptionTier ?? ""
4234
+ );
4235
+ await this.sendInitialMemo(jobId, fareAmount, subscriptionRequired, {
4236
+ name: this.name,
4237
+ requirement: finalServiceRequirement,
4238
+ priceValue: effectivePrice,
4239
+ priceType: effectivePriceType
4240
+ });
4241
+ return jobId;
4242
+ }
4243
+ validateRequest(serviceRequirement) {
4107
4244
  if (this.providerAddress === this.acpClient.walletAddress) {
4108
4245
  throw new acpError_default(
4109
4246
  "Provider address cannot be the same as the client address"
@@ -4111,84 +4248,147 @@ var AcpJobOffering = class {
4111
4248
  }
4112
4249
  if (this.requirement && typeof this.requirement === "object") {
4113
4250
  const validator = this.ajv.compile(this.requirement);
4114
- const valid = validator(serviceRequirement);
4115
- if (!valid) {
4251
+ if (!validator(serviceRequirement)) {
4116
4252
  throw new acpError_default(this.ajv.errorsText(validator.errors));
4117
4253
  }
4118
4254
  }
4119
- const finalServiceRequirement = {
4120
- name: this.name,
4121
- requirement: serviceRequirement,
4122
- priceValue: this.price,
4123
- priceType: this.priceType
4124
- };
4125
- const fareAmount = new FareAmount(
4126
- this.priceType === "fixed" /* FIXED */ ? this.price : 0,
4127
- this.acpContractClient.config.baseFare
4128
- );
4129
- const account = await this.acpClient.getByClientAndProvider(
4255
+ }
4256
+ isSubscriptionRequired(preferredSubscriptionTier) {
4257
+ const hasSubscriptionTiers = this.subscriptionTiers.length > 0;
4258
+ return preferredSubscriptionTier != null || this.priceType === "subscription" /* SUBSCRIPTION */ && hasSubscriptionTiers;
4259
+ }
4260
+ validateSubscriptionTier(preferredSubscriptionTier) {
4261
+ if (!preferredSubscriptionTier) return;
4262
+ if (this.subscriptionTiers.length === 0) {
4263
+ throw new acpError_default(
4264
+ `Offering "${this.name}" does not support subscription tiers`
4265
+ );
4266
+ }
4267
+ if (!this.subscriptionTiers.includes(preferredSubscriptionTier)) {
4268
+ throw new acpError_default(
4269
+ `Preferred subscription tier "${preferredSubscriptionTier}" is not offered. Available: ${this.subscriptionTiers.join(", ")}`
4270
+ );
4271
+ }
4272
+ }
4273
+ /**
4274
+ * Resolve the account to use for the job.
4275
+ *
4276
+ * For non-subscription jobs: returns the existing account if found.
4277
+ * For subscription jobs, priority:
4278
+ * 1. Valid account matching preferred tier
4279
+ * 2. Any valid (non-expired) account
4280
+ * 3. Expired/unactivated account (expiryAt = 0) to reuse
4281
+ * 4. null — createJob will create a new one
4282
+ */
4283
+ async resolveAccount(subscriptionRequired, preferredSubscriptionTier) {
4284
+ const raw = await this.acpClient.getByClientAndProvider(
4130
4285
  this.acpContractClient.walletAddress,
4131
4286
  this.providerAddress,
4132
- this.acpContractClient
4287
+ this.acpContractClient,
4288
+ subscriptionRequired ? this.name : void 0
4133
4289
  );
4290
+ if (!subscriptionRequired) {
4291
+ if (!(raw instanceof AcpAccount)) return null;
4292
+ const meta = raw.metadata;
4293
+ if (meta && typeof meta === "object" && meta.name) return null;
4294
+ return raw;
4295
+ }
4296
+ const subscriptionCheck = raw && typeof raw === "object" && "accounts" in raw ? raw : null;
4297
+ if (!subscriptionCheck) return null;
4298
+ const now = Math.floor(Date.now() / 1e3);
4299
+ const allAccounts = subscriptionCheck.accounts ?? [];
4300
+ const matchedAccount = this.findPreferredAccount(allAccounts, preferredSubscriptionTier, now) ?? allAccounts.find((a) => a.expiryAt != null && a.expiryAt > now) ?? allAccounts.find((a) => a.expiryAt == null || a.expiryAt === 0);
4301
+ if (!matchedAccount) return null;
4302
+ return new AcpAccount(
4303
+ this.acpContractClient,
4304
+ matchedAccount.id,
4305
+ matchedAccount.clientAddress ?? this.acpContractClient.walletAddress,
4306
+ matchedAccount.providerAddress ?? this.providerAddress,
4307
+ matchedAccount.metadata,
4308
+ matchedAccount.expiryAt
4309
+ );
4310
+ }
4311
+ findPreferredAccount(accounts, preferredTier, now) {
4312
+ if (!preferredTier) return void 0;
4313
+ return accounts.find((a) => {
4314
+ if (a.expiryAt == null || a.expiryAt <= now) return false;
4315
+ const meta = typeof a.metadata === "string" ? (() => {
4316
+ try {
4317
+ return JSON.parse(a.metadata);
4318
+ } catch {
4319
+ return {};
4320
+ }
4321
+ })() : a.metadata ?? {};
4322
+ return meta?.name === preferredTier;
4323
+ });
4324
+ }
4325
+ async createJob(account, evaluatorAddress, expiredAt, fareAmount, subscriptionRequired, subscriptionTier) {
4134
4326
  const isV1 = [
4135
4327
  baseSepoliaAcpConfig.contractAddress,
4136
4328
  baseSepoliaAcpX402Config.contractAddress,
4137
4329
  baseAcpConfig.contractAddress,
4138
4330
  baseAcpX402Config.contractAddress
4139
4331
  ].includes(this.acpContractClient.config.contractAddress);
4140
- let createJobPayload;
4141
4332
  const chainId = this.acpContractClient.config.chain.id;
4142
4333
  const isUsdcPaymentToken = USDC_TOKEN_ADDRESS[chainId].toLowerCase() === fareAmount.fare.contractAddress.toLowerCase();
4143
4334
  const isX402Job = this.acpContractClient.config.x402Config && isUsdcPaymentToken;
4144
- if (isV1 || !account) {
4145
- createJobPayload = this.acpContractClient.createJob(
4146
- this.providerAddress,
4147
- evaluatorAddress || this.acpContractClient.walletAddress,
4148
- expiredAt,
4149
- fareAmount.fare.contractAddress,
4150
- fareAmount.amount,
4151
- "",
4152
- isX402Job
4153
- );
4154
- } else {
4155
- createJobPayload = this.acpContractClient.createJobWithAccount(
4156
- account.id,
4157
- evaluatorAddress || zeroAddress2,
4158
- fareAmount.amount,
4159
- fareAmount.fare.contractAddress,
4160
- expiredAt,
4161
- isX402Job
4162
- );
4163
- }
4164
- const { userOpHash } = await this.acpContractClient.handleOperation([
4165
- createJobPayload
4335
+ const budget = subscriptionRequired ? 0n : fareAmount.amount;
4336
+ const subscriptionMetadata = subscriptionRequired ? JSON.stringify({ name: subscriptionTier }) : "";
4337
+ const operation = isV1 || !account ? this.acpContractClient.createJob(
4338
+ this.providerAddress,
4339
+ evaluatorAddress || this.acpContractClient.walletAddress,
4340
+ expiredAt,
4341
+ fareAmount.fare.contractAddress,
4342
+ budget,
4343
+ subscriptionMetadata,
4344
+ isX402Job
4345
+ ) : this.acpContractClient.createJobWithAccount(
4346
+ account.id,
4347
+ evaluatorAddress || zeroAddress2,
4348
+ budget,
4349
+ fareAmount.fare.contractAddress,
4350
+ expiredAt,
4351
+ isX402Job
4352
+ );
4353
+ const { userOpHash, txnHash } = await this.acpContractClient.handleOperation([
4354
+ operation
4166
4355
  ]);
4167
- const jobId = await this.acpContractClient.getJobId(
4356
+ return this.acpContractClient.getJobId(
4168
4357
  userOpHash,
4169
4358
  this.acpContractClient.walletAddress,
4170
4359
  this.providerAddress
4171
4360
  );
4361
+ }
4362
+ async sendInitialMemo(jobId, fareAmount, subscriptionRequired, serviceRequirement) {
4172
4363
  const payloads = [];
4173
- const setBudgetWithPaymentTokenPayload = this.acpContractClient.setBudgetWithPaymentToken(
4174
- jobId,
4175
- fareAmount.amount,
4176
- fareAmount.fare.contractAddress
4177
- );
4178
- if (setBudgetWithPaymentTokenPayload) {
4179
- payloads.push(setBudgetWithPaymentTokenPayload);
4364
+ if (!subscriptionRequired) {
4365
+ const setBudgetPayload = this.acpContractClient.setBudgetWithPaymentToken(
4366
+ jobId,
4367
+ fareAmount.amount,
4368
+ fareAmount.fare.contractAddress
4369
+ );
4370
+ if (setBudgetPayload) {
4371
+ payloads.push(setBudgetPayload);
4372
+ }
4373
+ }
4374
+ let content = JSON.stringify(serviceRequirement);
4375
+ if (this.isPrivate) {
4376
+ const memoContent = await this.acpClient.createMemoContent(
4377
+ jobId,
4378
+ content
4379
+ );
4380
+ content = memoContent.url;
4180
4381
  }
4181
4382
  payloads.push(
4182
4383
  this.acpContractClient.createMemo(
4183
4384
  jobId,
4184
- JSON.stringify(finalServiceRequirement),
4185
- 0 /* MESSAGE */,
4385
+ content,
4386
+ this.isPrivate ? 4 /* OBJECT_URL */ : 0 /* MESSAGE */,
4186
4387
  true,
4187
4388
  1 /* NEGOTIATION */
4188
4389
  )
4189
4390
  );
4190
4391
  await this.acpContractClient.handleOperation(payloads);
4191
- return jobId;
4192
4392
  }
4193
4393
  };
4194
4394
  var acpJobOffering_default = AcpJobOffering;
@@ -4196,7 +4396,7 @@ var acpJobOffering_default = AcpJobOffering;
4196
4396
  // src/acpJob.ts
4197
4397
  import * as util from "util";
4198
4398
  var AcpJob = class {
4199
- constructor(acpClient, id, clientAddress, providerAddress, evaluatorAddress, price, priceTokenAddress, memos, phase, context, contractAddress, _deliverable, netPayableAmount) {
4399
+ constructor(acpClient, id, clientAddress, providerAddress, evaluatorAddress, price, priceTokenAddress, memos, phase, context, contractAddress, netPayableAmount) {
4200
4400
  this.acpClient = acpClient;
4201
4401
  this.id = id;
4202
4402
  this.clientAddress = clientAddress;
@@ -4208,10 +4408,10 @@ var AcpJob = class {
4208
4408
  this.phase = phase;
4209
4409
  this.context = context;
4210
4410
  this.contractAddress = contractAddress;
4211
- this._deliverable = _deliverable;
4212
4411
  this.netPayableAmount = netPayableAmount;
4213
4412
  this.priceType = "fixed" /* FIXED */;
4214
4413
  this.priceValue = 0;
4414
+ this.isPrivate = false;
4215
4415
  const content = this.memos.find(
4216
4416
  (m) => m.nextPhase === 1 /* NEGOTIATION */
4217
4417
  )?.content;
@@ -4234,6 +4434,9 @@ var AcpJob = class {
4234
4434
  if (contentObj.priceValue) {
4235
4435
  this.priceValue = contentObj.priceValue || this.price;
4236
4436
  }
4437
+ if (contentObj.isPrivate) {
4438
+ this.isPrivate = contentObj.isPrivate;
4439
+ }
4237
4440
  }
4238
4441
  get acpContractClient() {
4239
4442
  return this.acpClient.contractClientByAddress(this.contractAddress);
@@ -4268,27 +4471,49 @@ var AcpJob = class {
4268
4471
  get latestMemo() {
4269
4472
  return this.memos[this.memos.length - 1];
4270
4473
  }
4271
- async getDeliverable() {
4272
- if (!this._deliverable) {
4474
+ getDeliverable() {
4475
+ const deliverableMemo = this.memos.find(
4476
+ (m) => m.nextPhase === 4 /* COMPLETED */
4477
+ );
4478
+ if (!deliverableMemo) {
4273
4479
  return null;
4274
4480
  }
4275
- if (typeof this._deliverable !== "string") {
4276
- return this._deliverable;
4277
- }
4278
- const regex = /api\/memo-contents\/([0-9]+)$/;
4279
- const result = this._deliverable?.match(regex);
4280
- if (!result) {
4281
- return this._deliverable;
4282
- }
4283
- const deliverable = await this.acpClient.getMemoContent(this._deliverable);
4284
- return tryParseJson(deliverable) || deliverable;
4481
+ return tryParseJson(deliverableMemo.content) || deliverableMemo.content;
4285
4482
  }
4286
4483
  async createRequirement(content) {
4287
4484
  const operations = [];
4485
+ let finalContent = content;
4486
+ if (this.isPrivate) {
4487
+ const memoContent = await this.acpClient.createMemoContent(
4488
+ this.id,
4489
+ content
4490
+ );
4491
+ finalContent = memoContent.url;
4492
+ }
4288
4493
  operations.push(
4289
4494
  this.acpContractClient.createMemo(
4290
4495
  this.id,
4291
- content,
4496
+ finalContent,
4497
+ this.isPrivate ? 4 /* OBJECT_URL */ : 0 /* MESSAGE */,
4498
+ true,
4499
+ 2 /* TRANSACTION */
4500
+ )
4501
+ );
4502
+ return await this.acpContractClient.handleOperation(operations);
4503
+ }
4504
+ async acceptRequirement(memo, reason) {
4505
+ const operations = [];
4506
+ operations.push(
4507
+ this.acpContractClient.signMemo(
4508
+ memo.id,
4509
+ true,
4510
+ reason ?? "Requirement accepted"
4511
+ )
4512
+ );
4513
+ operations.push(
4514
+ this.acpContractClient.createMemo(
4515
+ this.id,
4516
+ reason ?? "Proceeding to delivery",
4292
4517
  0 /* MESSAGE */,
4293
4518
  true,
4294
4519
  2 /* TRANSACTION */
@@ -4296,7 +4521,38 @@ var AcpJob = class {
4296
4521
  );
4297
4522
  return await this.acpContractClient.handleOperation(operations);
4298
4523
  }
4299
- async createPayableRequirement(content, type, amount, recipient, expiredAt = new Date(Date.now() + 1e3 * 60 * 5)) {
4524
+ async createPayableRequirement(content, type, amount, recipient, expiredAt = new Date(Date.now() + 1e3 * 60 * 5), duration) {
4525
+ if (type === 11 /* PAYABLE_REQUEST_SUBSCRIPTION */ && !duration) {
4526
+ throw new acpError_default("Duration is required for subscription payment requests");
4527
+ }
4528
+ if (type === 11 /* PAYABLE_REQUEST_SUBSCRIPTION */) {
4529
+ this.priceType = "subscription" /* SUBSCRIPTION */;
4530
+ if (this.priceValue !== 0) {
4531
+ throw new acpError_default(
4532
+ `Subscription payment request zero budget, got: ${this.priceValue}`
4533
+ );
4534
+ }
4535
+ let parsed;
4536
+ try {
4537
+ const result = JSON.parse(content);
4538
+ if (typeof result !== "object" || result === null || Array.isArray(result)) {
4539
+ throw new Error();
4540
+ }
4541
+ parsed = result;
4542
+ } catch {
4543
+ throw new acpError_default(
4544
+ `Subscription memo content must be a JSON object string, got: ${content}`
4545
+ );
4546
+ }
4547
+ const missing = ["name", "duration", "price"].filter(
4548
+ (k) => !(k in parsed)
4549
+ );
4550
+ if (missing.length > 0) {
4551
+ throw new acpError_default(
4552
+ `Subscription memo content is missing required fields: ${missing.join(", ")}`
4553
+ );
4554
+ }
4555
+ }
4300
4556
  const operations = [];
4301
4557
  if (type === 8 /* PAYABLE_TRANSFER_ESCROW */) {
4302
4558
  operations.push(
@@ -4308,11 +4564,34 @@ var AcpJob = class {
4308
4564
  }
4309
4565
  const feeAmount = new FareAmount(0, this.acpContractClient.config.baseFare);
4310
4566
  const isPercentagePricing = this.priceType === "percentage" /* PERCENTAGE */;
4311
- if (amount.fare.chainId && amount.fare.chainId !== this.acpContractClient.config.chain.id) {
4567
+ let finalContent = content;
4568
+ if (this.isPrivate) {
4569
+ const memoContent = await this.acpClient.createMemoContent(
4570
+ this.id,
4571
+ content
4572
+ );
4573
+ finalContent = memoContent.url;
4574
+ }
4575
+ if (type === 11 /* PAYABLE_REQUEST_SUBSCRIPTION */) {
4312
4576
  operations.push(
4313
- this.acpContractClient.createCrossChainPayableMemo(
4577
+ this.acpContractClient.createSubscriptionMemo(
4314
4578
  this.id,
4315
4579
  content,
4580
+ amount.amount,
4581
+ recipient,
4582
+ isPercentagePricing ? BigInt(Math.round(this.priceValue * 1e4)) : feeAmount.amount,
4583
+ isPercentagePricing ? 3 /* PERCENTAGE_FEE */ : 0 /* NO_FEE */,
4584
+ duration,
4585
+ 2 /* TRANSACTION */,
4586
+ expiredAt,
4587
+ amount.fare.contractAddress
4588
+ )
4589
+ );
4590
+ } else if (amount.fare.chainId && amount.fare.chainId !== this.acpContractClient.config.chain.id) {
4591
+ operations.push(
4592
+ this.acpContractClient.createCrossChainPayableMemo(
4593
+ this.id,
4594
+ finalContent,
4316
4595
  amount.fare.contractAddress,
4317
4596
  amount.amount,
4318
4597
  recipient,
@@ -4328,7 +4607,7 @@ var AcpJob = class {
4328
4607
  operations.push(
4329
4608
  this.acpContractClient.createPayableMemo(
4330
4609
  this.id,
4331
- content,
4610
+ finalContent,
4332
4611
  amount.amount,
4333
4612
  recipient,
4334
4613
  isPercentagePricing ? BigInt(Math.round(this.priceValue * 1e4)) : feeAmount.amount,
@@ -4359,14 +4638,15 @@ var AcpJob = class {
4359
4638
  memo.payableDetails.token,
4360
4639
  this.config
4361
4640
  ) : new FareAmount(0, this.baseFare);
4362
- const totalAmount = baseFareAmount.fare.contractAddress === transferAmount.fare.contractAddress ? baseFareAmount.add(transferAmount) : baseFareAmount;
4641
+ const sameToken = baseFareAmount.fare.contractAddress.toLowerCase() === transferAmount.fare.contractAddress.toLowerCase();
4642
+ const totalAmount = sameToken ? baseFareAmount.add(transferAmount) : baseFareAmount;
4363
4643
  operations.push(
4364
4644
  this.acpContractClient.approveAllowance(
4365
4645
  totalAmount.amount,
4366
4646
  this.baseFare.contractAddress
4367
4647
  )
4368
4648
  );
4369
- if (baseFareAmount.fare.contractAddress !== transferAmount.fare.contractAddress) {
4649
+ if (!sameToken) {
4370
4650
  operations.push(
4371
4651
  this.acpContractClient.approveAllowance(
4372
4652
  transferAmount.amount,
@@ -4375,11 +4655,19 @@ var AcpJob = class {
4375
4655
  );
4376
4656
  }
4377
4657
  operations.push(this.acpContractClient.signMemo(memo.id, true, reason));
4658
+ let finalContent = `Payment made. ${reason ?? ""}`.trim();
4659
+ if (this.isPrivate) {
4660
+ const memoContent = await this.acpClient.createMemoContent(
4661
+ this.id,
4662
+ finalContent
4663
+ );
4664
+ finalContent = memoContent.url;
4665
+ }
4378
4666
  operations.push(
4379
4667
  this.acpContractClient.createMemo(
4380
4668
  this.id,
4381
- `Payment made. ${reason ?? ""}`.trim(),
4382
- 0 /* MESSAGE */,
4669
+ finalContent,
4670
+ this.isPrivate ? 4 /* OBJECT_URL */ : 0 /* MESSAGE */,
4383
4671
  true,
4384
4672
  3 /* EVALUATION */
4385
4673
  )
@@ -4464,12 +4752,20 @@ var AcpJob = class {
4464
4752
  }
4465
4753
  return await latestMemo.sign(false, memoContent);
4466
4754
  }
4755
+ let finalContent = memoContent;
4756
+ if (this.isPrivate) {
4757
+ const memoContent2 = await this.acpClient.createMemoContent(
4758
+ this.id,
4759
+ finalContent
4760
+ );
4761
+ finalContent = memoContent2.url;
4762
+ }
4467
4763
  const operations = [];
4468
4764
  operations.push(
4469
4765
  this.acpContractClient.createMemo(
4470
4766
  this.id,
4471
- memoContent,
4472
- 0 /* MESSAGE */,
4767
+ finalContent,
4768
+ this.isPrivate ? 4 /* OBJECT_URL */ : 0 /* MESSAGE */,
4473
4769
  true,
4474
4770
  5 /* REJECTED */
4475
4771
  )
@@ -4486,10 +4782,18 @@ var AcpJob = class {
4486
4782
  amount.fare.contractAddress
4487
4783
  )
4488
4784
  );
4785
+ let finalContent = memoContent;
4786
+ if (this.isPrivate) {
4787
+ const memoContent2 = await this.acpClient.createMemoContent(
4788
+ this.id,
4789
+ finalContent
4790
+ );
4791
+ finalContent = memoContent2.url;
4792
+ }
4489
4793
  operations.push(
4490
4794
  this.acpContractClient.createPayableMemo(
4491
4795
  this.id,
4492
- memoContent,
4796
+ finalContent,
4493
4797
  amount.amount,
4494
4798
  this.clientAddress,
4495
4799
  feeAmount.amount,
@@ -4560,19 +4864,70 @@ var AcpJob = class {
4560
4864
  );
4561
4865
  return await this.acpContractClient.handleOperation(operations);
4562
4866
  }
4867
+ async paySubscription(reason) {
4868
+ if (this.phase === 6 /* EXPIRED */) {
4869
+ throw new acpError_default("Job has expired, cannot process subscription payment");
4870
+ }
4871
+ if (this.phase === 4 /* COMPLETED */) {
4872
+ throw new acpError_default(
4873
+ "Job is already completed, cannot process subscription payment"
4874
+ );
4875
+ }
4876
+ const memo = this.memos.find(
4877
+ (m) => m.type === 11 /* PAYABLE_REQUEST_SUBSCRIPTION */
4878
+ );
4879
+ if (!memo) {
4880
+ throw new acpError_default("No subscription payment request memo found");
4881
+ }
4882
+ if (!memo.payableDetails) {
4883
+ throw new acpError_default("Subscription memo has no payable details");
4884
+ }
4885
+ const operations = [];
4886
+ operations.push(
4887
+ this.acpContractClient.approveAllowance(
4888
+ memo.payableDetails.amount,
4889
+ memo.payableDetails.token
4890
+ )
4891
+ );
4892
+ operations.push(
4893
+ this.acpContractClient.signMemo(
4894
+ memo.id,
4895
+ true,
4896
+ reason || "Subscription payment approved"
4897
+ )
4898
+ );
4899
+ operations.push(
4900
+ this.acpContractClient.createMemo(
4901
+ this.id,
4902
+ `Subscription payment made. ${reason ?? ""}`.trim(),
4903
+ 0 /* MESSAGE */,
4904
+ true,
4905
+ memo.nextPhase
4906
+ )
4907
+ );
4908
+ return await this.acpContractClient.handleOperation(operations);
4909
+ }
4563
4910
  async evaluate(accept, reason) {
4564
4911
  if (this.latestMemo?.nextPhase !== 4 /* COMPLETED */) {
4565
4912
  throw new acpError_default("No evaluation memo found");
4566
4913
  }
4567
4914
  const memo = this.latestMemo;
4568
- await memo.sign(accept, reason);
4915
+ return await memo.sign(accept, reason);
4569
4916
  }
4570
4917
  async createNotification(content) {
4571
4918
  const operations = [];
4919
+ let finalContent = content;
4920
+ if (this.isPrivate) {
4921
+ const memoContent = await this.acpClient.createMemoContent(
4922
+ this.id,
4923
+ content
4924
+ );
4925
+ finalContent = memoContent.url;
4926
+ }
4572
4927
  operations.push(
4573
4928
  this.acpContractClient.createMemo(
4574
4929
  this.id,
4575
- content,
4930
+ finalContent,
4576
4931
  9 /* NOTIFICATION */,
4577
4932
  true,
4578
4933
  4 /* COMPLETED */
@@ -4590,10 +4945,18 @@ var AcpJob = class {
4590
4945
  );
4591
4946
  const feeAmount = new FareAmount(0, this.acpContractClient.config.baseFare);
4592
4947
  const isPercentagePricing = this.priceType === "percentage" /* PERCENTAGE */ && !skipFee;
4948
+ let finalContent = content;
4949
+ if (this.isPrivate) {
4950
+ const memoContent = await this.acpClient.createMemoContent(
4951
+ this.id,
4952
+ content
4953
+ );
4954
+ finalContent = memoContent.url;
4955
+ }
4593
4956
  operations.push(
4594
4957
  this.acpContractClient.createPayableMemo(
4595
4958
  this.id,
4596
- content,
4959
+ finalContent,
4597
4960
  amount.amount,
4598
4961
  this.clientAddress,
4599
4962
  isPercentagePricing ? BigInt(Math.round(this.priceValue * 1e4)) : feeAmount.amount,
@@ -4700,9 +5063,17 @@ var AcpJob = class {
4700
5063
  );
4701
5064
  const feeAmount = new FareAmount(0, this.acpContractClient.config.baseFare);
4702
5065
  const isPercentagePricing = this.priceType === "percentage" /* PERCENTAGE */ && !skipFee;
5066
+ let finalContent = content;
5067
+ if (this.isPrivate) {
5068
+ const memoContent = await this.acpClient.createMemoContent(
5069
+ this.id,
5070
+ content
5071
+ );
5072
+ finalContent = memoContent.url;
5073
+ }
4703
5074
  const createMemoOperation = this.acpContractClient.createCrossChainPayableMemo(
4704
5075
  this.id,
4705
- content,
5076
+ finalContent,
4706
5077
  amount.fare.contractAddress,
4707
5078
  amount.amount,
4708
5079
  recipient,
@@ -4737,9 +5108,9 @@ var acpJob_default = AcpJob;
4737
5108
 
4738
5109
  // src/acpMemo.ts
4739
5110
  import util2 from "util";
4740
- var AcpMemo = class {
4741
- constructor(contractClient, id, type, content, nextPhase, status, senderAddress, signedReason, expiry, payableDetails, txHash, signedTxHash, state) {
4742
- this.contractClient = contractClient;
5111
+ var AcpMemo = class _AcpMemo {
5112
+ constructor(acpClient, id, type, content, nextPhase, status, senderAddress, signedReason, expiry, payableDetails, txHash, signedTxHash, state) {
5113
+ this.acpClient = acpClient;
4743
5114
  this.id = id;
4744
5115
  this.type = type;
4745
5116
  this.content = content;
@@ -4757,8 +5128,39 @@ var AcpMemo = class {
4757
5128
  this.payableDetails.feeAmount = BigInt(this.payableDetails.feeAmount);
4758
5129
  }
4759
5130
  }
5131
+ static async build(acpClient, id, type, content, nextPhase, status, senderAddress, signedReason, expiry, payableDetails, txHash, signedTxHash, state) {
5132
+ let memoContent = content;
5133
+ const regex = /api\/memo-contents\/([0-9]+)$/;
5134
+ const result = memoContent.match(regex);
5135
+ if (result) {
5136
+ memoContent = await acpClient.getMemoContent(content);
5137
+ }
5138
+ return new _AcpMemo(
5139
+ acpClient,
5140
+ id,
5141
+ type,
5142
+ memoContent,
5143
+ nextPhase,
5144
+ status,
5145
+ senderAddress,
5146
+ signedReason,
5147
+ expiry,
5148
+ payableDetails,
5149
+ txHash,
5150
+ signedTxHash,
5151
+ state
5152
+ );
5153
+ }
5154
+ async getContent() {
5155
+ const regex = /api\/memo-contents\/([0-9]+)$/;
5156
+ const result = this.content.match(regex);
5157
+ if (!result) {
5158
+ return this.content;
5159
+ }
5160
+ return this.acpClient.getMemoContent(this.content);
5161
+ }
4760
5162
  async create(jobId, isSecured = true) {
4761
- return this.contractClient.createMemo(
5163
+ return this.acpClient.acpContractClient.createMemo(
4762
5164
  jobId,
4763
5165
  this.content,
4764
5166
  this.type,
@@ -4767,44 +5169,30 @@ var AcpMemo = class {
4767
5169
  );
4768
5170
  }
4769
5171
  async sign(approved, reason) {
4770
- const payload = this.contractClient.signMemo(this.id, approved, reason);
4771
- return await this.contractClient.handleOperation([payload]);
4772
- }
4773
- [util2.inspect.custom]() {
4774
- return {
4775
- id: this.id,
4776
- senderAddress: this.senderAddress,
4777
- type: MemoType[this.type],
4778
- status: this.status,
4779
- content: this.content,
4780
- signedReason: this.signedReason,
4781
- txHash: this.txHash,
4782
- signedTxHash: this.signedTxHash,
4783
- nextPhase: AcpJobPhases[this.nextPhase],
4784
- expiry: this.expiry,
4785
- payableDetails: this.payableDetails
4786
- };
4787
- }
4788
- };
4789
- var acpMemo_default = AcpMemo;
4790
-
4791
- // src/acpAccount.ts
4792
- var AcpAccount = class {
4793
- constructor(contractClient, id, clientAddress, providerAddress, metadata) {
4794
- this.contractClient = contractClient;
4795
- this.id = id;
4796
- this.clientAddress = clientAddress;
4797
- this.providerAddress = providerAddress;
4798
- this.metadata = metadata;
4799
- }
4800
- async updateMetadata(metadata) {
4801
- const hash = await this.contractClient.updateAccountMetadata(
5172
+ const payload = this.acpClient.acpContractClient.signMemo(
4802
5173
  this.id,
4803
- JSON.stringify(metadata)
5174
+ approved,
5175
+ reason
4804
5176
  );
4805
- return hash;
5177
+ return await this.acpClient.acpContractClient.handleOperation([payload]);
5178
+ }
5179
+ [util2.inspect.custom]() {
5180
+ return {
5181
+ id: this.id,
5182
+ senderAddress: this.senderAddress,
5183
+ type: MemoType[this.type],
5184
+ status: this.status,
5185
+ content: this.content,
5186
+ signedReason: this.signedReason,
5187
+ txHash: this.txHash,
5188
+ signedTxHash: this.signedTxHash,
5189
+ nextPhase: AcpJobPhases[this.nextPhase],
5190
+ expiry: this.expiry,
5191
+ payableDetails: this.payableDetails
5192
+ };
4806
5193
  }
4807
5194
  };
5195
+ var acpMemo_default = AcpMemo;
4808
5196
 
4809
5197
  // src/acpClient.ts
4810
5198
  import axios, { AxiosError } from "axios";
@@ -4821,6 +5209,7 @@ var AcpAgent = class {
4821
5209
  this.twitterHandle = args.twitterHandle;
4822
5210
  this.metrics = args.metrics;
4823
5211
  this.resources = args.resources;
5212
+ this.subscriptions = Object.freeze([...args.subscriptions ?? []]);
4824
5213
  }
4825
5214
  };
4826
5215
  var acpAgent_default = AcpAgent;
@@ -4931,6 +5320,7 @@ var AcpClient = class {
4931
5320
  });
4932
5321
  return response.data.data;
4933
5322
  } catch (err) {
5323
+ console.log("err->>", err.response.data);
4934
5324
  throw new acpError_default("Failed to verify auth challenge", err);
4935
5325
  }
4936
5326
  }
@@ -4985,18 +5375,26 @@ var AcpClient = class {
4985
5375
  socket.on("onEvaluate" /* ON_EVALUATE */, async (data, callback) => {
4986
5376
  callback(true);
4987
5377
  if (this.onEvaluate) {
4988
- const job = this._hydrateJob(data);
5378
+ const job = await this._hydrateJob(data);
4989
5379
  this.onEvaluate(job);
4990
5380
  }
4991
5381
  });
4992
5382
  socket.on("onNewTask" /* ON_NEW_TASK */, async (data, callback) => {
4993
5383
  callback(true);
4994
5384
  if (this.onNewTask) {
4995
- const job = this._hydrateJob(data);
4996
- this.onNewTask(
4997
- job,
4998
- job.memos.find((m) => m.id == data.memoToSign)
4999
- );
5385
+ const job = await this._hydrateJob(data);
5386
+ if (job.phase === 6 /* EXPIRED */) {
5387
+ console.warn(`onNewTask skipped for job ${data.id}: job has expired`);
5388
+ return;
5389
+ }
5390
+ try {
5391
+ await this.onNewTask(
5392
+ job,
5393
+ job.memos.find((m) => m.id == data.memoToSign)
5394
+ );
5395
+ } catch (err) {
5396
+ console.error(`onNewTask error for job ${data.id}:`, err);
5397
+ }
5000
5398
  }
5001
5399
  });
5002
5400
  const cleanup = async () => {
@@ -5023,8 +5421,6 @@ var AcpClient = class {
5023
5421
  errCallback(err);
5024
5422
  } else if (err.response?.data.error?.message) {
5025
5423
  throw new acpError_default(err.response?.data.error.message);
5026
- } else {
5027
- throw new acpError_default(`Failed to fetch ${url}: ${err.message}`, err);
5028
5424
  }
5029
5425
  } else {
5030
5426
  throw new acpError_default(
@@ -5034,10 +5430,10 @@ var AcpClient = class {
5034
5430
  }
5035
5431
  }
5036
5432
  }
5037
- _hydrateMemo(memo, contractClient) {
5433
+ async _hydrateMemo(memo) {
5038
5434
  try {
5039
- return new acpMemo_default(
5040
- contractClient,
5435
+ return await acpMemo_default.build(
5436
+ this,
5041
5437
  memo.id,
5042
5438
  memo.memoType,
5043
5439
  memo.content,
@@ -5045,7 +5441,7 @@ var AcpClient = class {
5045
5441
  memo.status,
5046
5442
  memo.senderAddress,
5047
5443
  memo.signedReason,
5048
- memo.expiry ? new Date(Number(memo.expiry) * 1e3) : void 0,
5444
+ memo.expiry ? new Date(parseInt(memo.expiry) * 1e3) : void 0,
5049
5445
  memo.payableDetails,
5050
5446
  memo.txHash,
5051
5447
  memo.signedTxHash,
@@ -5055,7 +5451,7 @@ var AcpClient = class {
5055
5451
  throw new acpError_default(`Failed to hydrate memo ${memo.id}`, err);
5056
5452
  }
5057
5453
  }
5058
- _hydrateJob(job) {
5454
+ async _hydrateJob(job) {
5059
5455
  try {
5060
5456
  return new acpJob_default(
5061
5457
  this,
@@ -5065,31 +5461,27 @@ var AcpClient = class {
5065
5461
  job.evaluatorAddress,
5066
5462
  job.price,
5067
5463
  job.priceTokenAddress,
5068
- job.memos.map(
5069
- (memo) => this._hydrateMemo(
5070
- memo,
5071
- this.contractClientByAddress(job.contractAddress)
5072
- )
5073
- ),
5464
+ await Promise.all(job.memos.map((memo) => this._hydrateMemo(memo))),
5074
5465
  job.phase,
5075
5466
  job.context,
5076
5467
  job.contractAddress,
5077
- job.deliverable,
5078
5468
  job.netPayableAmount
5079
5469
  );
5080
5470
  } catch (err) {
5081
5471
  throw new acpError_default(`Failed to hydrate job ${job.id}`, err);
5082
5472
  }
5083
5473
  }
5084
- _hydrateJobs(rawJobs, options) {
5085
- const jobs = rawJobs.map((job) => {
5086
- try {
5087
- return this._hydrateJob(job);
5088
- } catch (err) {
5089
- console.warn(`${options?.logPrefix ?? "Skipped"}`, err);
5090
- return null;
5091
- }
5092
- });
5474
+ async _hydrateJobs(rawJobs, options) {
5475
+ const jobs = await Promise.all(
5476
+ rawJobs.map((job) => {
5477
+ try {
5478
+ return this._hydrateJob(job);
5479
+ } catch (err) {
5480
+ console.warn(`${options?.logPrefix ?? "Skipped"}`, err);
5481
+ return null;
5482
+ }
5483
+ })
5484
+ );
5093
5485
  return jobs.filter((job) => !!job);
5094
5486
  }
5095
5487
  _hydrateAgent(agent) {
@@ -5118,14 +5510,17 @@ var AcpClient = class {
5118
5510
  offering.requiredFunds,
5119
5511
  offering.slaMinutes,
5120
5512
  offering.requirement,
5121
- offering.deliverable
5513
+ offering.deliverable,
5514
+ offering.subscriptionTiers ?? [],
5515
+ offering.isPrivate
5122
5516
  );
5123
5517
  }),
5124
5518
  contractAddress: agent.contractAddress,
5125
5519
  twitterHandle: agent.twitterHandle,
5126
5520
  walletAddress: agent.walletAddress,
5127
5521
  metrics: agent.metrics,
5128
- resources: agent.resources
5522
+ resources: agent.resources,
5523
+ subscriptions: agent.subscriptions ?? []
5129
5524
  });
5130
5525
  }
5131
5526
  async browseAgents(keyword, options = {}) {
@@ -5174,65 +5569,87 @@ var AcpClient = class {
5174
5569
  return this._hydrateAgent(agent);
5175
5570
  });
5176
5571
  }
5177
- async initiateJob(providerAddress, serviceRequirement, fareAmount, evaluatorAddress, expiredAt = new Date(Date.now() + 1e3 * 60 * 60 * 24)) {
5572
+ async initiateJob(providerAddress, serviceRequirement, fareAmount, evaluatorAddress, expiredAt = new Date(Date.now() + 1e3 * 60 * 60 * 24), offeringName, preferredSubscriptionTier) {
5178
5573
  if (providerAddress === this.walletAddress) {
5179
5574
  throw new acpError_default(
5180
5575
  "Provider address cannot be the same as the client address"
5181
5576
  );
5182
5577
  }
5183
- const account = await this.getByClientAndProvider(
5184
- this.walletAddress,
5578
+ const subscriptionRequired = preferredSubscriptionTier != null;
5579
+ const { account } = await this._resolveSubscriptionAccount(
5185
5580
  providerAddress,
5186
- this.acpContractClient
5581
+ offeringName,
5582
+ preferredSubscriptionTier
5187
5583
  );
5584
+ const budget = subscriptionRequired ? 0n : fareAmount.amount;
5585
+ const subscriptionMetadata = subscriptionRequired ? JSON.stringify({ name: preferredSubscriptionTier ?? "" }) : "";
5188
5586
  const isV1 = [
5189
5587
  baseSepoliaAcpConfig.contractAddress,
5190
5588
  baseSepoliaAcpX402Config.contractAddress,
5191
5589
  baseAcpConfig.contractAddress,
5192
5590
  baseAcpX402Config.contractAddress
5193
5591
  ].includes(this.acpContractClient.config.contractAddress);
5194
- const defaultEvaluatorAddress = isV1 && !evaluatorAddress ? this.walletAddress : zeroAddress3;
5592
+ const resolvedEvaluator = evaluatorAddress || (isV1 ? this.walletAddress : zeroAddress3);
5195
5593
  const chainId = this.acpContractClient.config.chain.id;
5196
- const isUsdcPaymentToken = USDC_TOKEN_ADDRESS[chainId].toLowerCase() === fareAmount.fare.contractAddress.toLowerCase();
5197
- const isX402Job = this.acpContractClient.config.x402Config && isUsdcPaymentToken;
5198
- const createJobPayload = isV1 || !account ? this.acpContractClient.createJob(
5199
- providerAddress,
5200
- evaluatorAddress || defaultEvaluatorAddress,
5201
- expiredAt,
5202
- fareAmount.fare.contractAddress,
5203
- fareAmount.amount,
5204
- "",
5205
- isX402Job
5206
- ) : this.acpContractClient.createJobWithAccount(
5207
- account.id,
5208
- evaluatorAddress || defaultEvaluatorAddress,
5209
- fareAmount.amount,
5210
- fareAmount.fare.contractAddress,
5211
- expiredAt,
5212
- isX402Job
5213
- );
5214
- const { userOpHash } = await this.acpContractClient.handleOperation([
5215
- createJobPayload
5216
- ]);
5594
+ const isX402Job = this.acpContractClient.config.x402Config && USDC_TOKEN_ADDRESS[chainId].toLowerCase() === fareAmount.fare.contractAddress.toLowerCase();
5595
+ const createJobOperations = [];
5596
+ if (isV1 || !account) {
5597
+ createJobOperations.push(
5598
+ this.acpContractClient.createJob(
5599
+ providerAddress,
5600
+ resolvedEvaluator,
5601
+ expiredAt,
5602
+ fareAmount.fare.contractAddress,
5603
+ budget,
5604
+ subscriptionMetadata,
5605
+ isX402Job
5606
+ )
5607
+ );
5608
+ } else {
5609
+ createJobOperations.push(
5610
+ this.acpContractClient.createJobWithAccount(
5611
+ account.id,
5612
+ resolvedEvaluator,
5613
+ budget,
5614
+ fareAmount.fare.contractAddress,
5615
+ expiredAt,
5616
+ isX402Job
5617
+ )
5618
+ );
5619
+ }
5620
+ const { userOpHash } = await this.acpContractClient.handleOperation(createJobOperations);
5217
5621
  const jobId = await this.acpContractClient.getJobId(
5218
5622
  userOpHash,
5219
5623
  this.walletAddress,
5220
5624
  providerAddress
5221
5625
  );
5222
5626
  const payloads = [];
5223
- const setBudgetWithPaymentTokenPayload = this.acpContractClient.setBudgetWithPaymentToken(
5224
- jobId,
5225
- fareAmount.amount,
5226
- fareAmount.fare.contractAddress
5227
- );
5228
- if (setBudgetWithPaymentTokenPayload) {
5229
- payloads.push(setBudgetWithPaymentTokenPayload);
5627
+ if (!subscriptionRequired) {
5628
+ const setBudgetPayload = this.acpContractClient.setBudgetWithPaymentToken(
5629
+ jobId,
5630
+ fareAmount.amount,
5631
+ fareAmount.fare.contractAddress
5632
+ );
5633
+ if (setBudgetPayload) {
5634
+ payloads.push(setBudgetPayload);
5635
+ }
5636
+ }
5637
+ const memoPayload = subscriptionRequired && typeof serviceRequirement === "object" ? preparePayload({
5638
+ ...serviceRequirement,
5639
+ priceValue: 0,
5640
+ priceType: "subscription" /* SUBSCRIPTION */
5641
+ }) : preparePayload(serviceRequirement);
5642
+ const isPrivate = typeof serviceRequirement === "object" && "isPrivate" in serviceRequirement && serviceRequirement.isPrivate;
5643
+ let content = memoPayload;
5644
+ if (isPrivate) {
5645
+ const memoContent = await this.createMemoContent(jobId, memoPayload);
5646
+ content = memoContent.url;
5230
5647
  }
5231
5648
  payloads.push(
5232
5649
  this.acpContractClient.createMemo(
5233
5650
  jobId,
5234
- preparePayload(serviceRequirement),
5235
- 0 /* MESSAGE */,
5651
+ isPrivate ? content : memoPayload,
5652
+ isPrivate ? 4 /* OBJECT_URL */ : 0 /* MESSAGE */,
5236
5653
  true,
5237
5654
  1 /* NEGOTIATION */
5238
5655
  )
@@ -5290,10 +5707,7 @@ var AcpClient = class {
5290
5707
  if (!memo) {
5291
5708
  return null;
5292
5709
  }
5293
- return this._hydrateMemo(
5294
- memo,
5295
- this.contractClientByAddress(memo.contractAddress)
5296
- );
5710
+ return this._hydrateMemo(memo);
5297
5711
  }
5298
5712
  async getAgent(walletAddress, options = {}) {
5299
5713
  const params = {
@@ -5321,32 +5735,195 @@ var AcpClient = class {
5321
5735
  account.id,
5322
5736
  account.clientAddress,
5323
5737
  account.providerAddress,
5324
- account.metadata
5738
+ account.metadata,
5739
+ account.expiryAt
5325
5740
  );
5326
5741
  }
5327
- async getByClientAndProvider(clientAddress, providerAddress, acpContractClient) {
5328
- const response = await this._fetch(
5329
- `/accounts/client/${clientAddress}/provider/${providerAddress}`,
5330
- "GET",
5331
- {},
5332
- {},
5333
- (err) => {
5334
- if (err.response?.status === 404) {
5335
- return;
5336
- }
5337
- throw new acpError_default("Failed to get account by client and provider", err);
5742
+ /**
5743
+ * Gets account or subscription data for a client–provider pair.
5744
+ * When offeringName is provided, the backend may return subscription tiers and accounts
5745
+ * (ISubscriptionCheckResponse). When not provided, returns a single AcpAccount or null.
5746
+ */
5747
+ async getByClientAndProvider(clientAddress, providerAddress, acpContractClient, offeringName) {
5748
+ let endpoint = `/accounts/client/${clientAddress}/provider/${providerAddress}`;
5749
+ if (offeringName) {
5750
+ endpoint = `/accounts/sub/client/${clientAddress}/provider/${providerAddress}`;
5751
+ }
5752
+ const response = await this._fetch(endpoint, "GET", {}, {}, (err) => {
5753
+ if (err.response?.status === 404) {
5754
+ return;
5338
5755
  }
5339
- );
5756
+ throw new acpError_default("Failed to get account by client and provider", err);
5757
+ });
5340
5758
  if (!response) {
5341
5759
  return null;
5342
5760
  }
5761
+ if (typeof response === "object" && "accounts" in response && Array.isArray(response.accounts)) {
5762
+ const sub = response;
5763
+ sub.accounts = sub.accounts.map((a) => ({
5764
+ ...a,
5765
+ expiryAt: a.expiryAt ?? a.expiry
5766
+ }));
5767
+ return sub;
5768
+ }
5769
+ const account = response;
5770
+ const expiryAt = account.expiryAt ?? account.expiry;
5343
5771
  return new AcpAccount(
5344
5772
  acpContractClient || this.contractClients[0],
5345
- response.id,
5346
- response.clientAddress,
5347
- response.providerAddress,
5348
- response.metadata
5773
+ account.id,
5774
+ account.clientAddress,
5775
+ account.providerAddress,
5776
+ account.metadata,
5777
+ expiryAt
5778
+ );
5779
+ }
5780
+ /**
5781
+ * Narrows a backend response to ISubscriptionCheckResponse if it has an accounts array.
5782
+ */
5783
+ _asSubscriptionCheck(raw) {
5784
+ return raw && typeof raw === "object" && "accounts" in raw ? raw : null;
5785
+ }
5786
+ /**
5787
+ * Resolve the account to use for the job.
5788
+ *
5789
+ * For subscription jobs, priority:
5790
+ * 1. Valid account matching preferred tier
5791
+ * 2. Any valid (non-expired) account
5792
+ * 3. Unactivated account (expiryAt = 0) to reuse
5793
+ * 4. null — createJob will create a new one
5794
+ */
5795
+ async _resolveSubscriptionAccount(providerAddress, offeringName, preferredSubscriptionTier) {
5796
+ if (!offeringName) return { account: null };
5797
+ const raw = await this.getByClientAndProvider(
5798
+ this.walletAddress,
5799
+ providerAddress,
5800
+ this.acpContractClient,
5801
+ offeringName
5802
+ );
5803
+ const subscriptionCheck = raw && typeof raw === "object" && "accounts" in raw ? raw : null;
5804
+ if (!subscriptionCheck) return { account: null };
5805
+ const now = Math.floor(Date.now() / 1e3);
5806
+ const allAccounts = subscriptionCheck.accounts ?? [];
5807
+ const matchedAccount = this._findPreferredAccount(allAccounts, preferredSubscriptionTier, now) ?? allAccounts.find((a) => a.expiryAt != null && a.expiryAt > now) ?? allAccounts.find((a) => a.expiryAt == null || a.expiryAt === 0);
5808
+ if (!matchedAccount) return { account: null };
5809
+ return {
5810
+ account: new AcpAccount(
5811
+ this.acpContractClient,
5812
+ matchedAccount.id,
5813
+ matchedAccount.clientAddress ?? this.walletAddress,
5814
+ matchedAccount.providerAddress ?? providerAddress,
5815
+ matchedAccount.metadata,
5816
+ matchedAccount.expiryAt
5817
+ )
5818
+ };
5819
+ }
5820
+ _findPreferredAccount(accounts, preferredTier, now) {
5821
+ if (!preferredTier) return void 0;
5822
+ return accounts.find((a) => {
5823
+ if (a.expiryAt == null || a.expiryAt <= now) return false;
5824
+ const meta = typeof a.metadata === "string" ? (() => {
5825
+ try {
5826
+ return JSON.parse(a.metadata);
5827
+ } catch {
5828
+ return {};
5829
+ }
5830
+ })() : a.metadata ?? {};
5831
+ return meta?.name === preferredTier;
5832
+ });
5833
+ }
5834
+ /**
5835
+ * Returns the first subscription account with expiryAt > now, or null.
5836
+ */
5837
+ _getValidSubscriptionAccountFromResponse(response, acpContractClient) {
5838
+ const now = Math.floor(Date.now() / 1e3);
5839
+ const valid = response.accounts?.find(
5840
+ (a) => a.expiryAt != null && a.expiryAt > now
5841
+ );
5842
+ if (!valid) return null;
5843
+ return new AcpAccount(
5844
+ acpContractClient,
5845
+ valid.id,
5846
+ valid.clientAddress,
5847
+ valid.providerAddress,
5848
+ valid.metadata,
5849
+ valid.expiryAt
5850
+ );
5851
+ }
5852
+ /**
5853
+ * Seller-facing: determines whether to create a subscription payment request memo.
5854
+ * Call this when handling a new job (e.g. in REQUEST phase); then branch on
5855
+ * needsSubscriptionPayment and use tier when true.
5856
+ */
5857
+ async getSubscriptionPaymentRequirement(clientAddress, providerAddress, offeringName) {
5858
+ let raw;
5859
+ try {
5860
+ raw = await this.getByClientAndProvider(
5861
+ clientAddress,
5862
+ providerAddress,
5863
+ void 0,
5864
+ offeringName
5865
+ );
5866
+ } catch {
5867
+ return {
5868
+ needsSubscriptionPayment: false,
5869
+ action: "no_subscription_required"
5870
+ };
5871
+ }
5872
+ const response = this._asSubscriptionCheck(raw);
5873
+ if (!response?.accounts?.length) {
5874
+ return {
5875
+ needsSubscriptionPayment: false,
5876
+ action: "no_subscription_required"
5877
+ };
5878
+ }
5879
+ const now = Math.floor(Date.now() / 1e3);
5880
+ const hasValidSubscription = response.accounts.some(
5881
+ (a) => a.expiryAt != null && a.expiryAt > now
5349
5882
  );
5883
+ if (hasValidSubscription) {
5884
+ return {
5885
+ needsSubscriptionPayment: false,
5886
+ action: "valid_subscription"
5887
+ };
5888
+ }
5889
+ const firstAccount = response.accounts[0];
5890
+ const tier = {
5891
+ name: firstAccount.metadata?.name ?? "",
5892
+ price: firstAccount.metadata?.price ?? 0,
5893
+ duration: firstAccount.metadata?.duration ?? 0
5894
+ };
5895
+ return {
5896
+ needsSubscriptionPayment: true,
5897
+ tier
5898
+ };
5899
+ }
5900
+ async getValidSubscriptionAccount(providerAddress, offeringName, clientAddress, acpContractClient) {
5901
+ const raw = await this.getByClientAndProvider(
5902
+ clientAddress,
5903
+ providerAddress,
5904
+ acpContractClient,
5905
+ offeringName
5906
+ );
5907
+ const subscriptionCheck = this._asSubscriptionCheck(raw);
5908
+ if (!subscriptionCheck) return null;
5909
+ const contractClient = acpContractClient || this.contractClients[0];
5910
+ const account = this._getValidSubscriptionAccountFromResponse(
5911
+ subscriptionCheck,
5912
+ contractClient
5913
+ );
5914
+ if (account) return account;
5915
+ const legacy = subscriptionCheck;
5916
+ if (legacy.subscriptionRequired && legacy.hasValidSubscription && legacy.account) {
5917
+ return new AcpAccount(
5918
+ contractClient,
5919
+ legacy.account.id,
5920
+ legacy.account.clientAddress,
5921
+ legacy.account.providerAddress,
5922
+ legacy.account.metadata,
5923
+ legacy.account.expiryAt
5924
+ );
5925
+ }
5926
+ return null;
5350
5927
  }
5351
5928
  async createMemoContent(jobId, content) {
5352
5929
  const response = await this._fetch(
@@ -5662,9 +6239,7 @@ var AcpContractClient = class _AcpContractClient extends baseAcpContractClient_d
5662
6239
  throw new acpError_default(`Failed to send user operation`, finalError);
5663
6240
  }
5664
6241
  async getJobId(createJobUserOpHash, clientAddress, providerAddress) {
5665
- const result = await this.sessionKeyClient.getUserOperationReceipt(
5666
- createJobUserOpHash
5667
- );
6242
+ const result = await this.sessionKeyClient.getUserOperationReceipt(createJobUserOpHash);
5668
6243
  if (!result) {
5669
6244
  throw new acpError_default("Failed to get user operation receipt");
5670
6245
  }
@@ -6536,6 +7111,7 @@ var MEMO_MANAGER_ABI = [
6536
7111
  { inputs: [], name: "InvalidMemoState", type: "error" },
6537
7112
  { inputs: [], name: "InvalidMemoStateTransition", type: "error" },
6538
7113
  { inputs: [], name: "InvalidMemoType", type: "error" },
7114
+ { inputs: [], name: "InvalidSubscriptionDuration", type: "error" },
6539
7115
  { inputs: [], name: "JobAlreadyCompleted", type: "error" },
6540
7116
  { inputs: [], name: "JobDoesNotExist", type: "error" },
6541
7117
  { inputs: [], name: "MemoAlreadyApproved", type: "error" },
@@ -6571,6 +7147,7 @@ var MEMO_MANAGER_ABI = [
6571
7147
  { inputs: [], name: "ZeroAddressToken", type: "error" },
6572
7148
  { inputs: [], name: "ZeroAssetManagerAddress", type: "error" },
6573
7149
  { inputs: [], name: "ZeroJobManagerAddress", type: "error" },
7150
+ { inputs: [], name: "ZeroPaymentManagerAddress", type: "error" },
6574
7151
  {
6575
7152
  anonymous: false,
6576
7153
  inputs: [
@@ -6843,6 +7420,31 @@ var MEMO_MANAGER_ABI = [
6843
7420
  name: "RoleRevoked",
6844
7421
  type: "event"
6845
7422
  },
7423
+ {
7424
+ anonymous: false,
7425
+ inputs: [
7426
+ {
7427
+ indexed: true,
7428
+ internalType: "uint256",
7429
+ name: "memoId",
7430
+ type: "uint256"
7431
+ },
7432
+ {
7433
+ indexed: true,
7434
+ internalType: "uint256",
7435
+ name: "accountId",
7436
+ type: "uint256"
7437
+ },
7438
+ {
7439
+ indexed: false,
7440
+ internalType: "uint256",
7441
+ name: "duration",
7442
+ type: "uint256"
7443
+ }
7444
+ ],
7445
+ name: "SubscriptionActivated",
7446
+ type: "event"
7447
+ },
6846
7448
  {
6847
7449
  anonymous: false,
6848
7450
  inputs: [
@@ -6910,17 +7512,6 @@ var MEMO_MANAGER_ABI = [
6910
7512
  stateMutability: "view",
6911
7513
  type: "function"
6912
7514
  },
6913
- {
6914
- inputs: [
6915
- { internalType: "uint256[]", name: "memoIds", type: "uint256[]" },
6916
- { internalType: "bool", name: "approved", type: "bool" },
6917
- { internalType: "string", name: "reason", type: "string" }
6918
- ],
6919
- name: "bulkApproveMemos",
6920
- outputs: [],
6921
- stateMutability: "nonpayable",
6922
- type: "function"
6923
- },
6924
7515
  {
6925
7516
  inputs: [
6926
7517
  { internalType: "uint256", name: "memoId", type: "uint256" },
@@ -6998,9 +7589,40 @@ var MEMO_MANAGER_ABI = [
6998
7589
  type: "function"
6999
7590
  },
7000
7591
  {
7001
- inputs: [{ internalType: "uint256", name: "memoId", type: "uint256" }],
7002
- name: "emergencyApproveMemo",
7003
- outputs: [],
7592
+ inputs: [
7593
+ { internalType: "uint256", name: "jobId", type: "uint256" },
7594
+ { internalType: "address", name: "sender", type: "address" },
7595
+ { internalType: "string", name: "content", type: "string" },
7596
+ {
7597
+ components: [
7598
+ { internalType: "address", name: "token", type: "address" },
7599
+ { internalType: "uint256", name: "amount", type: "uint256" },
7600
+ { internalType: "address", name: "recipient", type: "address" },
7601
+ { internalType: "uint256", name: "feeAmount", type: "uint256" },
7602
+ {
7603
+ internalType: "enum ACPTypes.FeeType",
7604
+ name: "feeType",
7605
+ type: "uint8"
7606
+ },
7607
+ { internalType: "bool", name: "isExecuted", type: "bool" },
7608
+ { internalType: "uint256", name: "expiredAt", type: "uint256" },
7609
+ { internalType: "uint32", name: "lzSrcEid", type: "uint32" },
7610
+ { internalType: "uint32", name: "lzDstEid", type: "uint32" }
7611
+ ],
7612
+ internalType: "struct ACPTypes.PayableDetails",
7613
+ name: "payableDetails_",
7614
+ type: "tuple"
7615
+ },
7616
+ { internalType: "uint256", name: "duration", type: "uint256" },
7617
+ { internalType: "uint256", name: "expiredAt", type: "uint256" },
7618
+ {
7619
+ internalType: "enum ACPTypes.JobPhase",
7620
+ name: "nextPhase",
7621
+ type: "uint8"
7622
+ }
7623
+ ],
7624
+ name: "createSubscriptionMemo",
7625
+ outputs: [{ internalType: "uint256", name: "memoId", type: "uint256" }],
7004
7626
  stateMutability: "nonpayable",
7005
7627
  type: "function"
7006
7628
  },
@@ -7011,13 +7633,6 @@ var MEMO_MANAGER_ABI = [
7011
7633
  stateMutability: "nonpayable",
7012
7634
  type: "function"
7013
7635
  },
7014
- {
7015
- inputs: [],
7016
- name: "getAssetManager",
7017
- outputs: [{ internalType: "address", name: "", type: "address" }],
7018
- stateMutability: "view",
7019
- type: "function"
7020
- },
7021
7636
  {
7022
7637
  inputs: [
7023
7638
  { internalType: "uint256", name: "jobId", type: "uint256" },
@@ -7214,17 +7829,6 @@ var MEMO_MANAGER_ABI = [
7214
7829
  stateMutability: "view",
7215
7830
  type: "function"
7216
7831
  },
7217
- {
7218
- inputs: [{ internalType: "uint256", name: "memoId", type: "uint256" }],
7219
- name: "getMemoApprovalStatus",
7220
- outputs: [
7221
- { internalType: "bool", name: "isApproved", type: "bool" },
7222
- { internalType: "address", name: "approvedBy", type: "address" },
7223
- { internalType: "uint256", name: "approvedAt", type: "uint256" }
7224
- ],
7225
- stateMutability: "view",
7226
- type: "function"
7227
- },
7228
7832
  {
7229
7833
  inputs: [{ internalType: "uint256", name: "memoId", type: "uint256" }],
7230
7834
  name: "getMemoWithPayableDetails",
@@ -7287,34 +7891,6 @@ var MEMO_MANAGER_ABI = [
7287
7891
  stateMutability: "view",
7288
7892
  type: "function"
7289
7893
  },
7290
- {
7291
- inputs: [{ internalType: "uint256", name: "memoId", type: "uint256" }],
7292
- name: "getPayableDetails",
7293
- outputs: [
7294
- {
7295
- components: [
7296
- { internalType: "address", name: "token", type: "address" },
7297
- { internalType: "uint256", name: "amount", type: "uint256" },
7298
- { internalType: "address", name: "recipient", type: "address" },
7299
- { internalType: "uint256", name: "feeAmount", type: "uint256" },
7300
- {
7301
- internalType: "enum ACPTypes.FeeType",
7302
- name: "feeType",
7303
- type: "uint8"
7304
- },
7305
- { internalType: "bool", name: "isExecuted", type: "bool" },
7306
- { internalType: "uint256", name: "expiredAt", type: "uint256" },
7307
- { internalType: "uint32", name: "lzSrcEid", type: "uint32" },
7308
- { internalType: "uint32", name: "lzDstEid", type: "uint32" }
7309
- ],
7310
- internalType: "struct ACPTypes.PayableDetails",
7311
- name: "",
7312
- type: "tuple"
7313
- }
7314
- ],
7315
- stateMutability: "view",
7316
- type: "function"
7317
- },
7318
7894
  {
7319
7895
  inputs: [{ internalType: "bytes32", name: "role", type: "bytes32" }],
7320
7896
  name: "getRoleAdmin",
@@ -7353,16 +7929,6 @@ var MEMO_MANAGER_ABI = [
7353
7929
  stateMutability: "nonpayable",
7354
7930
  type: "function"
7355
7931
  },
7356
- {
7357
- inputs: [
7358
- { internalType: "uint256", name: "jobId", type: "uint256" },
7359
- { internalType: "address", name: "user", type: "address" }
7360
- ],
7361
- name: "isJobEvaluator",
7362
- outputs: [{ internalType: "bool", name: "", type: "bool" }],
7363
- stateMutability: "view",
7364
- type: "function"
7365
- },
7366
7932
  {
7367
7933
  inputs: [
7368
7934
  { internalType: "uint256", name: "memoId", type: "uint256" },
@@ -7534,23 +8100,16 @@ var MEMO_MANAGER_ABI = [
7534
8100
  },
7535
8101
  {
7536
8102
  inputs: [
7537
- {
7538
- internalType: "enum ACPTypes.MemoType",
7539
- name: "memoType",
7540
- type: "uint8"
7541
- },
7542
- { internalType: "uint256", name: "requiredApprovals_", type: "uint256" }
8103
+ { internalType: "address", name: "assetManager_", type: "address" }
7543
8104
  ],
7544
- name: "setApprovalRequirements",
8105
+ name: "setAssetManager",
7545
8106
  outputs: [],
7546
8107
  stateMutability: "nonpayable",
7547
8108
  type: "function"
7548
8109
  },
7549
8110
  {
7550
- inputs: [
7551
- { internalType: "address", name: "assetManager_", type: "address" }
7552
- ],
7553
- name: "setAssetManager",
8111
+ inputs: [{ internalType: "uint256", name: "memoId", type: "uint256" }],
8112
+ name: "setPayableDetailsExecuted",
7554
8113
  outputs: [],
7555
8114
  stateMutability: "nonpayable",
7556
8115
  type: "function"
@@ -7631,12 +8190,14 @@ var MEMO_MANAGER_ABI = [
7631
8190
  var memoManagerAbi_default = MEMO_MANAGER_ABI;
7632
8191
 
7633
8192
  // src/contractClients/acpContractClientV2.ts
8193
+ import { Attribution } from "ox/erc8021";
7634
8194
  var AcpContractClientV2 = class _AcpContractClientV2 extends baseAcpContractClient_default {
7635
- constructor(jobManagerAddress, memoManagerAddress, accountManagerAddress, agentWalletAddress, config = baseAcpConfigV2) {
8195
+ constructor(jobManagerAddress, memoManagerAddress, accountManagerAddress, agentWalletAddress, config = baseAcpConfigV2, builderCode) {
7636
8196
  super(agentWalletAddress, config);
7637
8197
  this.jobManagerAddress = jobManagerAddress;
7638
8198
  this.memoManagerAddress = memoManagerAddress;
7639
8199
  this.accountManagerAddress = accountManagerAddress;
8200
+ this.builderCode = builderCode;
7640
8201
  this.RETRY_CONFIG = {
7641
8202
  intervalMs: 200,
7642
8203
  multiplier: 1.1,
@@ -7644,7 +8205,7 @@ var AcpContractClientV2 = class _AcpContractClientV2 extends baseAcpContractClie
7644
8205
  };
7645
8206
  this._sessionKeyClients = {};
7646
8207
  }
7647
- static async build(walletPrivateKey, sessionEntityKeyId, agentWalletAddress, config = baseAcpConfigV2) {
8208
+ static async build(walletPrivateKey, sessionEntityKeyId, agentWalletAddress, config = baseAcpConfigV2, builderCode) {
7648
8209
  const publicClients = {};
7649
8210
  for (const chain of config.chains) {
7650
8211
  publicClients[chain.id] = createPublicClient4({
@@ -7685,7 +8246,8 @@ var AcpContractClientV2 = class _AcpContractClientV2 extends baseAcpContractClie
7685
8246
  memoManagerAddress.result,
7686
8247
  accountManagerAddress.result,
7687
8248
  agentWalletAddress,
7688
- config
8249
+ config,
8250
+ builderCode
7689
8251
  );
7690
8252
  acpContractClient.publicClients = publicClients;
7691
8253
  await acpContractClient.init(walletPrivateKey, sessionEntityKeyId);
@@ -7770,10 +8332,11 @@ var AcpContractClientV2 = class _AcpContractClientV2 extends baseAcpContractClie
7770
8332
  if (!sessionKeyClient) {
7771
8333
  throw new acpError_default("Session key client not initialized");
7772
8334
  }
8335
+ const dataSuffix = this.builderCode ? Attribution.toDataSuffix({ codes: [this.builderCode] }) : void 0;
7773
8336
  const basePayload = {
7774
8337
  uo: operations.map((operation) => ({
7775
8338
  target: operation.contractAddress,
7776
- data: operation.data,
8339
+ data: dataSuffix ? appendBuilderCodeData(operation.data, dataSuffix) : operation.data,
7777
8340
  value: operation.value
7778
8341
  }))
7779
8342
  };
@@ -7842,6 +8405,37 @@ var AcpContractClientV2 = class _AcpContractClientV2 extends baseAcpContractClie
7842
8405
  }
7843
8406
  return Number(createdJobEvent.args.jobId);
7844
8407
  }
8408
+ async getAccountIdFromUserOpHash(userOpHash) {
8409
+ const result = await this.sessionKeyClient.getUserOperationReceipt(
8410
+ userOpHash,
8411
+ "pending"
8412
+ );
8413
+ if (!result || !result.logs?.length) {
8414
+ return null;
8415
+ }
8416
+ const contractAddresses = [
8417
+ this.contractAddress.toLowerCase(),
8418
+ this.accountManagerAddress.toLowerCase()
8419
+ ];
8420
+ for (const log of result.logs) {
8421
+ if (!contractAddresses.includes(log.address?.toLowerCase())) {
8422
+ continue;
8423
+ }
8424
+ try {
8425
+ const decoded = decodeEventLog2({
8426
+ abi: this.abi,
8427
+ data: log.data,
8428
+ topics: log.topics
8429
+ });
8430
+ if (decoded.eventName === "AccountCreated") {
8431
+ const args = decoded.args;
8432
+ if (args?.accountId != null) return Number(args.accountId);
8433
+ }
8434
+ } catch {
8435
+ }
8436
+ }
8437
+ return null;
8438
+ }
7845
8439
  async updateJobX402Nonce(jobId, nonce) {
7846
8440
  return await this.acpX402.updateJobNonce(jobId, nonce);
7847
8441
  }
@@ -7895,6 +8489,7 @@ var acpContractClientV2_default = AcpContractClientV2;
7895
8489
  var index_default = acpClient_default;
7896
8490
  export {
7897
8491
  acpAbi_default as ACP_ABI,
8492
+ AcpAccount,
7898
8493
  acpAgent_default as AcpAgent,
7899
8494
  AcpAgentSort,
7900
8495
  acpContractClient_default as AcpContractClient,
@@ -7913,6 +8508,7 @@ export {
7913
8508
  FareAmount,
7914
8509
  FareBigInt,
7915
8510
  MemoType,
8511
+ PriceType,
7916
8512
  baseAcpConfig,
7917
8513
  baseAcpConfigV2,
7918
8514
  baseAcpX402Config,