@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.js CHANGED
@@ -35,7 +35,7 @@ var require_package = __commonJS({
35
35
  "package.json"(exports2, module2) {
36
36
  module2.exports = {
37
37
  name: "@virtuals-protocol/acp-node",
38
- version: "0.3.0-beta.37",
38
+ version: "0.3.0-beta.39",
39
39
  main: "./dist/index.js",
40
40
  module: "./dist/index.mjs",
41
41
  types: "./dist/index.d.ts",
@@ -70,6 +70,7 @@ var require_package = __commonJS({
70
70
  ajv: "^8.17.1",
71
71
  axios: "^1.13.2",
72
72
  "jwt-decode": "^4.0.0",
73
+ ox: "^0.13.1",
73
74
  "socket.io-client": "^4.8.1",
74
75
  tsup: "^8.5.0",
75
76
  viem: "^2.28.2"
@@ -85,6 +86,7 @@ var require_package = __commonJS({
85
86
  var index_exports = {};
86
87
  __export(index_exports, {
87
88
  ACP_ABI: () => acpAbi_default,
89
+ AcpAccount: () => AcpAccount,
88
90
  AcpAgent: () => acpAgent_default,
89
91
  AcpAgentSort: () => AcpAgentSort,
90
92
  AcpContractClient: () => acpContractClient_default,
@@ -103,6 +105,7 @@ __export(index_exports, {
103
105
  FareAmount: () => FareAmount,
104
106
  FareBigInt: () => FareBigInt,
105
107
  MemoType: () => MemoType,
108
+ PriceType: () => PriceType,
106
109
  baseAcpConfig: () => baseAcpConfig,
107
110
  baseAcpConfigV2: () => baseAcpConfigV2,
108
111
  baseAcpX402Config: () => baseAcpX402Config,
@@ -1382,7 +1385,7 @@ var FareAmount = class extends FareAmountBase {
1382
1385
  super(fare.formatAmount(truncateTo6Decimals(fareAmount.toString())), fare);
1383
1386
  }
1384
1387
  add(other) {
1385
- if (this.fare.contractAddress !== other.fare.contractAddress) {
1388
+ if (this.fare.contractAddress.toLowerCase() !== other.fare.contractAddress.toLowerCase()) {
1386
1389
  throw new Error("Token addresses do not match");
1387
1390
  }
1388
1391
  return new FareBigInt(this.amount + other.amount, this.fare);
@@ -1415,6 +1418,10 @@ var ACP_V2_ABI = [
1415
1418
  name: "AccessControlUnauthorizedAccount",
1416
1419
  type: "error"
1417
1420
  },
1421
+ { inputs: [], name: "AccountAlreadySubscribed", type: "error" },
1422
+ { inputs: [], name: "AccountDoesNotExist", type: "error" },
1423
+ { inputs: [], name: "AccountManagerNotSet", type: "error" },
1424
+ { inputs: [], name: "AccountNotActive", type: "error" },
1418
1425
  {
1419
1426
  inputs: [{ internalType: "address", name: "target", type: "address" }],
1420
1427
  name: "AddressEmptyCode",
@@ -1425,6 +1432,13 @@ var ACP_V2_ABI = [
1425
1432
  name: "AddressInsufficientBalance",
1426
1433
  type: "error"
1427
1434
  },
1435
+ { inputs: [], name: "AmountMustBeGreaterThanZero", type: "error" },
1436
+ { inputs: [], name: "AmountOrFeeRequired", type: "error" },
1437
+ { inputs: [], name: "AssetManagerNotSet", type: "error" },
1438
+ { inputs: [], name: "BudgetNotReceived", type: "error" },
1439
+ { inputs: [], name: "CannotSetBudgetOnSubscriptionJob", type: "error" },
1440
+ { inputs: [], name: "DestinationEndpointRequired", type: "error" },
1441
+ { inputs: [], name: "DurationMustBeGreaterThanZero", type: "error" },
1428
1442
  {
1429
1443
  inputs: [
1430
1444
  { internalType: "address", name: "implementation", type: "address" }
@@ -1434,22 +1448,46 @@ var ACP_V2_ABI = [
1434
1448
  },
1435
1449
  { inputs: [], name: "ERC1967NonPayable", type: "error" },
1436
1450
  { inputs: [], name: "EnforcedPause", type: "error" },
1451
+ { inputs: [], name: "EvaluatorFeeTooHigh", type: "error" },
1437
1452
  { inputs: [], name: "ExpectedPause", type: "error" },
1453
+ { inputs: [], name: "ExpiredAtMustBeInFuture", type: "error" },
1454
+ { inputs: [], name: "ExpiryTooShort", type: "error" },
1438
1455
  { inputs: [], name: "FailedInnerCall", type: "error" },
1456
+ { inputs: [], name: "InvalidCrossChainMemoType", type: "error" },
1439
1457
  { inputs: [], name: "InvalidInitialization", type: "error" },
1458
+ { inputs: [], name: "InvalidMemoType", type: "error" },
1459
+ { inputs: [], name: "InvalidModuleType", type: "error" },
1460
+ { inputs: [], name: "InvalidPaymentToken", type: "error" },
1461
+ { inputs: [], name: "InvalidRecipient", type: "error" },
1462
+ { inputs: [], name: "JobManagerNotSet", type: "error" },
1463
+ { inputs: [], name: "MemoManagerNotSet", type: "error" },
1440
1464
  { inputs: [], name: "NotInitializing", type: "error" },
1465
+ { inputs: [], name: "OnlyClient", type: "error" },
1466
+ { inputs: [], name: "OnlyMemoManager", type: "error" },
1467
+ { inputs: [], name: "OnlyProvider", type: "error" },
1468
+ { inputs: [], name: "PaymentManagerNotSet", type: "error" },
1469
+ { inputs: [], name: "PlatformFeeTooHigh", type: "error" },
1441
1470
  { inputs: [], name: "ReentrancyGuardReentrantCall", type: "error" },
1442
1471
  {
1443
1472
  inputs: [{ internalType: "address", name: "token", type: "address" }],
1444
1473
  name: "SafeERC20FailedOperation",
1445
1474
  type: "error"
1446
1475
  },
1476
+ { inputs: [], name: "SubscriptionAccountExpired", type: "error" },
1477
+ { inputs: [], name: "SubscriptionJobMustHaveZeroBudget", type: "error" },
1478
+ { inputs: [], name: "SubscriptionJobMustHaveZeroBudgetMemo", type: "error" },
1479
+ { inputs: [], name: "TokenAddressRequired", type: "error" },
1480
+ { inputs: [], name: "TokenMustBeERC20", type: "error" },
1447
1481
  { inputs: [], name: "UUPSUnauthorizedCallContext", type: "error" },
1448
1482
  {
1449
1483
  inputs: [{ internalType: "bytes32", name: "slot", type: "bytes32" }],
1450
1484
  name: "UUPSUnsupportedProxiableUUID",
1451
1485
  type: "error"
1452
1486
  },
1487
+ { inputs: [], name: "UnableToRefund", type: "error" },
1488
+ { inputs: [], name: "Unauthorized", type: "error" },
1489
+ { inputs: [], name: "ZeroAddress", type: "error" },
1490
+ { inputs: [], name: "ZeroAddressProvider", type: "error" },
1453
1491
  {
1454
1492
  anonymous: false,
1455
1493
  inputs: [
@@ -1489,56 +1527,6 @@ var ACP_V2_ABI = [
1489
1527
  name: "AccountStatusUpdated",
1490
1528
  type: "event"
1491
1529
  },
1492
- {
1493
- anonymous: false,
1494
- inputs: [
1495
- {
1496
- indexed: false,
1497
- internalType: "uint256",
1498
- name: "jobId",
1499
- type: "uint256"
1500
- },
1501
- {
1502
- indexed: true,
1503
- internalType: "address",
1504
- name: "evaluator",
1505
- type: "address"
1506
- },
1507
- {
1508
- indexed: false,
1509
- internalType: "uint256",
1510
- name: "evaluatorFee",
1511
- type: "uint256"
1512
- }
1513
- ],
1514
- name: "ClaimedEvaluatorFee",
1515
- type: "event"
1516
- },
1517
- {
1518
- anonymous: false,
1519
- inputs: [
1520
- {
1521
- indexed: false,
1522
- internalType: "uint256",
1523
- name: "jobId",
1524
- type: "uint256"
1525
- },
1526
- {
1527
- indexed: true,
1528
- internalType: "address",
1529
- name: "provider",
1530
- type: "address"
1531
- },
1532
- {
1533
- indexed: false,
1534
- internalType: "uint256",
1535
- name: "providerFee",
1536
- type: "uint256"
1537
- }
1538
- ],
1539
- name: "ClaimedProviderFee",
1540
- type: "event"
1541
- },
1542
1530
  {
1543
1531
  anonymous: false,
1544
1532
  inputs: [
@@ -1590,31 +1578,6 @@ var ACP_V2_ABI = [
1590
1578
  name: "Paused",
1591
1579
  type: "event"
1592
1580
  },
1593
- {
1594
- anonymous: false,
1595
- inputs: [
1596
- {
1597
- indexed: false,
1598
- internalType: "uint256",
1599
- name: "jobId",
1600
- type: "uint256"
1601
- },
1602
- {
1603
- indexed: true,
1604
- internalType: "address",
1605
- name: "client",
1606
- type: "address"
1607
- },
1608
- {
1609
- indexed: false,
1610
- internalType: "uint256",
1611
- name: "amount",
1612
- type: "uint256"
1613
- }
1614
- ],
1615
- name: "RefundedBudget",
1616
- type: "event"
1617
- },
1618
1581
  {
1619
1582
  anonymous: false,
1620
1583
  inputs: [
@@ -1762,6 +1725,13 @@ var ACP_V2_ABI = [
1762
1725
  stateMutability: "nonpayable",
1763
1726
  type: "function"
1764
1727
  },
1728
+ {
1729
+ inputs: [{ internalType: "uint256", name: "jobId", type: "uint256" }],
1730
+ name: "claimBudgetFromMemoManager",
1731
+ outputs: [],
1732
+ stateMutability: "nonpayable",
1733
+ type: "function"
1734
+ },
1765
1735
  {
1766
1736
  inputs: [
1767
1737
  { internalType: "address", name: "provider", type: "address" },
@@ -1875,6 +1845,28 @@ var ACP_V2_ABI = [
1875
1845
  stateMutability: "nonpayable",
1876
1846
  type: "function"
1877
1847
  },
1848
+ {
1849
+ inputs: [
1850
+ { internalType: "uint256", name: "jobId", type: "uint256" },
1851
+ { internalType: "string", name: "content", type: "string" },
1852
+ { internalType: "address", name: "token", type: "address" },
1853
+ { internalType: "uint256", name: "amount", type: "uint256" },
1854
+ { internalType: "address", name: "recipient", type: "address" },
1855
+ { internalType: "uint256", name: "feeAmount", type: "uint256" },
1856
+ { internalType: "enum ACPTypes.FeeType", name: "feeType", type: "uint8" },
1857
+ { internalType: "uint256", name: "duration", type: "uint256" },
1858
+ { internalType: "uint256", name: "expiredAt", type: "uint256" },
1859
+ {
1860
+ internalType: "enum ACPTypes.JobPhase",
1861
+ name: "nextPhase",
1862
+ type: "uint8"
1863
+ }
1864
+ ],
1865
+ name: "createSubscriptionMemo",
1866
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
1867
+ stateMutability: "nonpayable",
1868
+ type: "function"
1869
+ },
1878
1870
  {
1879
1871
  inputs: [
1880
1872
  { internalType: "address", name: "provider", type: "address" },
@@ -1943,7 +1935,8 @@ var ACP_V2_ABI = [
1943
1935
  name: "completedJobCount",
1944
1936
  type: "uint256"
1945
1937
  },
1946
- { internalType: "bool", name: "isActive", type: "bool" }
1938
+ { internalType: "bool", name: "isActive", type: "bool" },
1939
+ { internalType: "uint256", name: "expiry", type: "uint256" }
1947
1940
  ],
1948
1941
  internalType: "struct ACPTypes.Account",
1949
1942
  name: "",
@@ -2100,13 +2093,6 @@ var ACP_V2_ABI = [
2100
2093
  stateMutability: "view",
2101
2094
  type: "function"
2102
2095
  },
2103
- {
2104
- inputs: [],
2105
- name: "getPhases",
2106
- outputs: [{ internalType: "string[7]", name: "", type: "string[7]" }],
2107
- stateMutability: "pure",
2108
- type: "function"
2109
- },
2110
2096
  {
2111
2097
  inputs: [{ internalType: "bytes32", name: "role", type: "bytes32" }],
2112
2098
  name: "getRoleAdmin",
@@ -2151,11 +2137,8 @@ var ACP_V2_ABI = [
2151
2137
  type: "function"
2152
2138
  },
2153
2139
  {
2154
- inputs: [
2155
- { internalType: "uint256", name: "jobId", type: "uint256" },
2156
- { internalType: "address", name: "account", type: "address" }
2157
- ],
2158
- name: "isJobEvaluator",
2140
+ inputs: [{ internalType: "uint256", name: "jobId", type: "uint256" }],
2141
+ name: "isSubscriptionJob",
2159
2142
  outputs: [{ internalType: "bool", name: "", type: "bool" }],
2160
2143
  stateMutability: "view",
2161
2144
  type: "function"
@@ -2263,6 +2246,13 @@ var ACP_V2_ABI = [
2263
2246
  stateMutability: "nonpayable",
2264
2247
  type: "function"
2265
2248
  },
2249
+ {
2250
+ inputs: [{ internalType: "uint256", name: "jobId", type: "uint256" }],
2251
+ name: "setupEscrowFromMemoManager",
2252
+ outputs: [],
2253
+ stateMutability: "nonpayable",
2254
+ type: "function"
2255
+ },
2266
2256
  {
2267
2257
  inputs: [
2268
2258
  { internalType: "uint256", name: "memoId", type: "uint256" },
@@ -2298,6 +2288,18 @@ var ACP_V2_ABI = [
2298
2288
  stateMutability: "nonpayable",
2299
2289
  type: "function"
2300
2290
  },
2291
+ {
2292
+ inputs: [
2293
+ { internalType: "uint256", name: "accountId", type: "uint256" },
2294
+ { internalType: "uint256", name: "duration", type: "uint256" },
2295
+ { internalType: "string", name: "metadata", type: "string" },
2296
+ { internalType: "address", name: "provider", type: "address" }
2297
+ ],
2298
+ name: "updateAccountSubscriptionFromMemoManager",
2299
+ outputs: [],
2300
+ stateMutability: "nonpayable",
2301
+ type: "function"
2302
+ },
2301
2303
  {
2302
2304
  inputs: [
2303
2305
  { internalType: "uint256", name: "evaluatorFeeBP_", type: "uint256" }
@@ -3645,6 +3647,7 @@ var MemoType = /* @__PURE__ */ ((MemoType3) => {
3645
3647
  MemoType3[MemoType3["PAYABLE_TRANSFER_ESCROW"] = 8] = "PAYABLE_TRANSFER_ESCROW";
3646
3648
  MemoType3[MemoType3["NOTIFICATION"] = 9] = "NOTIFICATION";
3647
3649
  MemoType3[MemoType3["PAYABLE_NOTIFICATION"] = 10] = "PAYABLE_NOTIFICATION";
3650
+ MemoType3[MemoType3["PAYABLE_REQUEST_SUBSCRIPTION"] = 11] = "PAYABLE_REQUEST_SUBSCRIPTION";
3648
3651
  return MemoType3;
3649
3652
  })(MemoType || {});
3650
3653
  var AcpJobPhases = /* @__PURE__ */ ((AcpJobPhases3) => {
@@ -3713,6 +3716,35 @@ ${JSON.stringify(
3713
3716
  );
3714
3717
  }
3715
3718
  }
3719
+ /**
3720
+ * Returns a createAccount operation payload if the contract supports it (V2).
3721
+ * Returns null for V1 or when the ABI does not include createAccount.
3722
+ */
3723
+ createAccount(providerAddress, metadata) {
3724
+ try {
3725
+ const hasCreateAccount = this.abi.some(
3726
+ (item) => item.type === "function" && item.name === "createAccount"
3727
+ );
3728
+ if (!hasCreateAccount) return null;
3729
+ const data = (0, import_viem2.encodeFunctionData)({
3730
+ abi: this.abi,
3731
+ functionName: "createAccount",
3732
+ args: [providerAddress, metadata]
3733
+ });
3734
+ return {
3735
+ data,
3736
+ contractAddress: this.contractAddress
3737
+ };
3738
+ } catch {
3739
+ return null;
3740
+ }
3741
+ }
3742
+ /**
3743
+ * Returns the new account id from a createAccount user op receipt, or null if not supported.
3744
+ */
3745
+ async getAccountIdFromUserOpHash(_userOpHash) {
3746
+ return null;
3747
+ }
3716
3748
  get walletAddress() {
3717
3749
  return this.agentWalletAddress;
3718
3750
  }
@@ -3805,6 +3837,33 @@ ${JSON.stringify(
3805
3837
  throw new acpError_default("Failed to create payable memo", error);
3806
3838
  }
3807
3839
  }
3840
+ createSubscriptionMemo(jobId, content, amountBaseUnit, recipient, feeAmountBaseUnit, feeType, duration, nextPhase, expiredAt, token = this.config.baseFare.contractAddress) {
3841
+ try {
3842
+ const data = (0, import_viem2.encodeFunctionData)({
3843
+ abi: this.abi,
3844
+ functionName: "createSubscriptionMemo",
3845
+ args: [
3846
+ jobId,
3847
+ content,
3848
+ token,
3849
+ amountBaseUnit,
3850
+ recipient,
3851
+ feeAmountBaseUnit,
3852
+ feeType,
3853
+ duration,
3854
+ Math.floor(expiredAt.getTime() / 1e3),
3855
+ nextPhase
3856
+ ]
3857
+ });
3858
+ const payload = {
3859
+ data,
3860
+ contractAddress: this.contractAddress
3861
+ };
3862
+ return payload;
3863
+ } catch (error) {
3864
+ throw new acpError_default("Failed to create subscription memo", error);
3865
+ }
3866
+ }
3808
3867
  createCrossChainPayableMemo(jobId, content, token, amountBaseUnit, recipient, feeAmountBaseUnit, feeType, type, expiredAt, nextPhase, destinationEid, secured = true) {
3809
3868
  try {
3810
3869
  const data = (0, import_viem2.encodeFunctionData)({
@@ -4107,12 +4166,48 @@ function getDestinationChainId(endpointId) {
4107
4166
  }
4108
4167
  throw new Error(`Unsupported endpoint ID: ${endpointId}`);
4109
4168
  }
4169
+ function appendBuilderCodeData(data, suffix) {
4170
+ const opDataByteLength = (data.length - 2) / 2;
4171
+ const suffixByteLength = (suffix.length - 2) / 2;
4172
+ const opDataPaddedSize = Math.ceil(opDataByteLength / 32) * 32;
4173
+ const suffixPaddedSize = Math.ceil(suffixByteLength / 32) * 32;
4174
+ const paddedData = (0, import_viem3.pad)(data, { size: opDataPaddedSize, dir: "right" });
4175
+ const paddedSuffix = (0, import_viem3.pad)(suffix, { size: suffixPaddedSize });
4176
+ return (0, import_viem3.concatHex)([paddedData, paddedSuffix]);
4177
+ }
4110
4178
 
4111
4179
  // src/acpJobOffering.ts
4112
4180
  var import_viem4 = require("viem");
4113
4181
  var import_ajv = __toESM(require("ajv"));
4182
+
4183
+ // src/acpAccount.ts
4184
+ var AcpAccount = class {
4185
+ constructor(contractClient, id, clientAddress, providerAddress, metadata, expiryAt) {
4186
+ this.contractClient = contractClient;
4187
+ this.id = id;
4188
+ this.clientAddress = clientAddress;
4189
+ this.providerAddress = providerAddress;
4190
+ this.metadata = metadata;
4191
+ this.expiryAt = expiryAt;
4192
+ }
4193
+ async updateMetadata(metadata) {
4194
+ const hash = await this.contractClient.updateAccountMetadata(
4195
+ this.id,
4196
+ JSON.stringify(metadata)
4197
+ );
4198
+ return hash;
4199
+ }
4200
+ };
4201
+
4202
+ // src/acpJobOffering.ts
4203
+ var PriceType = /* @__PURE__ */ ((PriceType2) => {
4204
+ PriceType2["FIXED"] = "fixed";
4205
+ PriceType2["PERCENTAGE"] = "percentage";
4206
+ PriceType2["SUBSCRIPTION"] = "subscription";
4207
+ return PriceType2;
4208
+ })(PriceType || {});
4114
4209
  var AcpJobOffering = class {
4115
- constructor(acpClient, acpContractClient, providerAddress, name, price, priceType, requiredFunds, slaMinutes, requirement, deliverable) {
4210
+ constructor(acpClient, acpContractClient, providerAddress, name, price, priceType = "fixed" /* FIXED */, requiredFunds, slaMinutes, requirement, deliverable, subscriptionTiers = [], isPrivate = false) {
4116
4211
  this.acpClient = acpClient;
4117
4212
  this.acpContractClient = acpContractClient;
4118
4213
  this.providerAddress = providerAddress;
@@ -4123,10 +4218,49 @@ var AcpJobOffering = class {
4123
4218
  this.slaMinutes = slaMinutes;
4124
4219
  this.requirement = requirement;
4125
4220
  this.deliverable = deliverable;
4221
+ this.subscriptionTiers = subscriptionTiers;
4222
+ this.isPrivate = isPrivate;
4126
4223
  this.ajv = new import_ajv.default({ allErrors: true });
4127
4224
  }
4128
- async initiateJob(serviceRequirement, evaluatorAddress) {
4129
- const expiredAt = new Date(Date.now() + this.slaMinutes * 60 * 1e3);
4225
+ async initiateJob(serviceRequirement, evaluatorAddress, expiredAt = new Date(Date.now() + this.slaMinutes * 60 * 1e3), preferredSubscriptionTier) {
4226
+ this.validateRequest(serviceRequirement);
4227
+ const finalServiceRequirement = {
4228
+ name: this.name,
4229
+ requirement: serviceRequirement,
4230
+ priceValue: this.price,
4231
+ priceType: this.priceType
4232
+ };
4233
+ const subscriptionRequired = this.isSubscriptionRequired(
4234
+ preferredSubscriptionTier
4235
+ );
4236
+ this.validateSubscriptionTier(preferredSubscriptionTier);
4237
+ const effectivePrice = subscriptionRequired ? 0 : this.price;
4238
+ const effectivePriceType = subscriptionRequired ? "subscription" /* SUBSCRIPTION */ : this.priceType === "subscription" /* SUBSCRIPTION */ ? "fixed" /* FIXED */ : this.priceType;
4239
+ const fareAmount = new FareAmount(
4240
+ effectivePriceType === "fixed" /* FIXED */ ? effectivePrice : 0,
4241
+ this.acpContractClient.config.baseFare
4242
+ );
4243
+ const account = await this.resolveAccount(
4244
+ subscriptionRequired,
4245
+ preferredSubscriptionTier
4246
+ );
4247
+ const jobId = await this.createJob(
4248
+ account,
4249
+ evaluatorAddress,
4250
+ expiredAt,
4251
+ fareAmount,
4252
+ subscriptionRequired,
4253
+ preferredSubscriptionTier ?? ""
4254
+ );
4255
+ await this.sendInitialMemo(jobId, fareAmount, subscriptionRequired, {
4256
+ name: this.name,
4257
+ requirement: finalServiceRequirement,
4258
+ priceValue: effectivePrice,
4259
+ priceType: effectivePriceType
4260
+ });
4261
+ return jobId;
4262
+ }
4263
+ validateRequest(serviceRequirement) {
4130
4264
  if (this.providerAddress === this.acpClient.walletAddress) {
4131
4265
  throw new acpError_default(
4132
4266
  "Provider address cannot be the same as the client address"
@@ -4134,84 +4268,147 @@ var AcpJobOffering = class {
4134
4268
  }
4135
4269
  if (this.requirement && typeof this.requirement === "object") {
4136
4270
  const validator = this.ajv.compile(this.requirement);
4137
- const valid = validator(serviceRequirement);
4138
- if (!valid) {
4271
+ if (!validator(serviceRequirement)) {
4139
4272
  throw new acpError_default(this.ajv.errorsText(validator.errors));
4140
4273
  }
4141
4274
  }
4142
- const finalServiceRequirement = {
4143
- name: this.name,
4144
- requirement: serviceRequirement,
4145
- priceValue: this.price,
4146
- priceType: this.priceType
4147
- };
4148
- const fareAmount = new FareAmount(
4149
- this.priceType === "fixed" /* FIXED */ ? this.price : 0,
4150
- this.acpContractClient.config.baseFare
4151
- );
4152
- const account = await this.acpClient.getByClientAndProvider(
4275
+ }
4276
+ isSubscriptionRequired(preferredSubscriptionTier) {
4277
+ const hasSubscriptionTiers = this.subscriptionTiers.length > 0;
4278
+ return preferredSubscriptionTier != null || this.priceType === "subscription" /* SUBSCRIPTION */ && hasSubscriptionTiers;
4279
+ }
4280
+ validateSubscriptionTier(preferredSubscriptionTier) {
4281
+ if (!preferredSubscriptionTier) return;
4282
+ if (this.subscriptionTiers.length === 0) {
4283
+ throw new acpError_default(
4284
+ `Offering "${this.name}" does not support subscription tiers`
4285
+ );
4286
+ }
4287
+ if (!this.subscriptionTiers.includes(preferredSubscriptionTier)) {
4288
+ throw new acpError_default(
4289
+ `Preferred subscription tier "${preferredSubscriptionTier}" is not offered. Available: ${this.subscriptionTiers.join(", ")}`
4290
+ );
4291
+ }
4292
+ }
4293
+ /**
4294
+ * Resolve the account to use for the job.
4295
+ *
4296
+ * For non-subscription jobs: returns the existing account if found.
4297
+ * For subscription jobs, priority:
4298
+ * 1. Valid account matching preferred tier
4299
+ * 2. Any valid (non-expired) account
4300
+ * 3. Expired/unactivated account (expiryAt = 0) to reuse
4301
+ * 4. null — createJob will create a new one
4302
+ */
4303
+ async resolveAccount(subscriptionRequired, preferredSubscriptionTier) {
4304
+ const raw = await this.acpClient.getByClientAndProvider(
4153
4305
  this.acpContractClient.walletAddress,
4154
4306
  this.providerAddress,
4155
- this.acpContractClient
4307
+ this.acpContractClient,
4308
+ subscriptionRequired ? this.name : void 0
4309
+ );
4310
+ if (!subscriptionRequired) {
4311
+ if (!(raw instanceof AcpAccount)) return null;
4312
+ const meta = raw.metadata;
4313
+ if (meta && typeof meta === "object" && meta.name) return null;
4314
+ return raw;
4315
+ }
4316
+ const subscriptionCheck = raw && typeof raw === "object" && "accounts" in raw ? raw : null;
4317
+ if (!subscriptionCheck) return null;
4318
+ const now = Math.floor(Date.now() / 1e3);
4319
+ const allAccounts = subscriptionCheck.accounts ?? [];
4320
+ 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);
4321
+ if (!matchedAccount) return null;
4322
+ return new AcpAccount(
4323
+ this.acpContractClient,
4324
+ matchedAccount.id,
4325
+ matchedAccount.clientAddress ?? this.acpContractClient.walletAddress,
4326
+ matchedAccount.providerAddress ?? this.providerAddress,
4327
+ matchedAccount.metadata,
4328
+ matchedAccount.expiryAt
4156
4329
  );
4330
+ }
4331
+ findPreferredAccount(accounts, preferredTier, now) {
4332
+ if (!preferredTier) return void 0;
4333
+ return accounts.find((a) => {
4334
+ if (a.expiryAt == null || a.expiryAt <= now) return false;
4335
+ const meta = typeof a.metadata === "string" ? (() => {
4336
+ try {
4337
+ return JSON.parse(a.metadata);
4338
+ } catch {
4339
+ return {};
4340
+ }
4341
+ })() : a.metadata ?? {};
4342
+ return meta?.name === preferredTier;
4343
+ });
4344
+ }
4345
+ async createJob(account, evaluatorAddress, expiredAt, fareAmount, subscriptionRequired, subscriptionTier) {
4157
4346
  const isV1 = [
4158
4347
  baseSepoliaAcpConfig.contractAddress,
4159
4348
  baseSepoliaAcpX402Config.contractAddress,
4160
4349
  baseAcpConfig.contractAddress,
4161
4350
  baseAcpX402Config.contractAddress
4162
4351
  ].includes(this.acpContractClient.config.contractAddress);
4163
- let createJobPayload;
4164
4352
  const chainId = this.acpContractClient.config.chain.id;
4165
4353
  const isUsdcPaymentToken = USDC_TOKEN_ADDRESS[chainId].toLowerCase() === fareAmount.fare.contractAddress.toLowerCase();
4166
4354
  const isX402Job = this.acpContractClient.config.x402Config && isUsdcPaymentToken;
4167
- if (isV1 || !account) {
4168
- createJobPayload = this.acpContractClient.createJob(
4169
- this.providerAddress,
4170
- evaluatorAddress || this.acpContractClient.walletAddress,
4171
- expiredAt,
4172
- fareAmount.fare.contractAddress,
4173
- fareAmount.amount,
4174
- "",
4175
- isX402Job
4176
- );
4177
- } else {
4178
- createJobPayload = this.acpContractClient.createJobWithAccount(
4179
- account.id,
4180
- evaluatorAddress || import_viem4.zeroAddress,
4181
- fareAmount.amount,
4182
- fareAmount.fare.contractAddress,
4183
- expiredAt,
4184
- isX402Job
4185
- );
4186
- }
4187
- const { userOpHash } = await this.acpContractClient.handleOperation([
4188
- createJobPayload
4355
+ const budget = subscriptionRequired ? 0n : fareAmount.amount;
4356
+ const subscriptionMetadata = subscriptionRequired ? JSON.stringify({ name: subscriptionTier }) : "";
4357
+ const operation = isV1 || !account ? this.acpContractClient.createJob(
4358
+ this.providerAddress,
4359
+ evaluatorAddress || this.acpContractClient.walletAddress,
4360
+ expiredAt,
4361
+ fareAmount.fare.contractAddress,
4362
+ budget,
4363
+ subscriptionMetadata,
4364
+ isX402Job
4365
+ ) : this.acpContractClient.createJobWithAccount(
4366
+ account.id,
4367
+ evaluatorAddress || import_viem4.zeroAddress,
4368
+ budget,
4369
+ fareAmount.fare.contractAddress,
4370
+ expiredAt,
4371
+ isX402Job
4372
+ );
4373
+ const { userOpHash, txnHash } = await this.acpContractClient.handleOperation([
4374
+ operation
4189
4375
  ]);
4190
- const jobId = await this.acpContractClient.getJobId(
4376
+ return this.acpContractClient.getJobId(
4191
4377
  userOpHash,
4192
4378
  this.acpContractClient.walletAddress,
4193
4379
  this.providerAddress
4194
4380
  );
4381
+ }
4382
+ async sendInitialMemo(jobId, fareAmount, subscriptionRequired, serviceRequirement) {
4195
4383
  const payloads = [];
4196
- const setBudgetWithPaymentTokenPayload = this.acpContractClient.setBudgetWithPaymentToken(
4197
- jobId,
4198
- fareAmount.amount,
4199
- fareAmount.fare.contractAddress
4200
- );
4201
- if (setBudgetWithPaymentTokenPayload) {
4202
- payloads.push(setBudgetWithPaymentTokenPayload);
4384
+ if (!subscriptionRequired) {
4385
+ const setBudgetPayload = this.acpContractClient.setBudgetWithPaymentToken(
4386
+ jobId,
4387
+ fareAmount.amount,
4388
+ fareAmount.fare.contractAddress
4389
+ );
4390
+ if (setBudgetPayload) {
4391
+ payloads.push(setBudgetPayload);
4392
+ }
4393
+ }
4394
+ let content = JSON.stringify(serviceRequirement);
4395
+ if (this.isPrivate) {
4396
+ const memoContent = await this.acpClient.createMemoContent(
4397
+ jobId,
4398
+ content
4399
+ );
4400
+ content = memoContent.url;
4203
4401
  }
4204
4402
  payloads.push(
4205
4403
  this.acpContractClient.createMemo(
4206
4404
  jobId,
4207
- JSON.stringify(finalServiceRequirement),
4208
- 0 /* MESSAGE */,
4405
+ content,
4406
+ this.isPrivate ? 4 /* OBJECT_URL */ : 0 /* MESSAGE */,
4209
4407
  true,
4210
4408
  1 /* NEGOTIATION */
4211
4409
  )
4212
4410
  );
4213
4411
  await this.acpContractClient.handleOperation(payloads);
4214
- return jobId;
4215
4412
  }
4216
4413
  };
4217
4414
  var acpJobOffering_default = AcpJobOffering;
@@ -4219,7 +4416,7 @@ var acpJobOffering_default = AcpJobOffering;
4219
4416
  // src/acpJob.ts
4220
4417
  var util = __toESM(require("util"));
4221
4418
  var AcpJob = class {
4222
- constructor(acpClient, id, clientAddress, providerAddress, evaluatorAddress, price, priceTokenAddress, memos, phase, context, contractAddress, _deliverable, netPayableAmount) {
4419
+ constructor(acpClient, id, clientAddress, providerAddress, evaluatorAddress, price, priceTokenAddress, memos, phase, context, contractAddress, netPayableAmount) {
4223
4420
  this.acpClient = acpClient;
4224
4421
  this.id = id;
4225
4422
  this.clientAddress = clientAddress;
@@ -4231,10 +4428,10 @@ var AcpJob = class {
4231
4428
  this.phase = phase;
4232
4429
  this.context = context;
4233
4430
  this.contractAddress = contractAddress;
4234
- this._deliverable = _deliverable;
4235
4431
  this.netPayableAmount = netPayableAmount;
4236
4432
  this.priceType = "fixed" /* FIXED */;
4237
4433
  this.priceValue = 0;
4434
+ this.isPrivate = false;
4238
4435
  const content = this.memos.find(
4239
4436
  (m) => m.nextPhase === 1 /* NEGOTIATION */
4240
4437
  )?.content;
@@ -4257,6 +4454,9 @@ var AcpJob = class {
4257
4454
  if (contentObj.priceValue) {
4258
4455
  this.priceValue = contentObj.priceValue || this.price;
4259
4456
  }
4457
+ if (contentObj.isPrivate) {
4458
+ this.isPrivate = contentObj.isPrivate;
4459
+ }
4260
4460
  }
4261
4461
  get acpContractClient() {
4262
4462
  return this.acpClient.contractClientByAddress(this.contractAddress);
@@ -4291,27 +4491,49 @@ var AcpJob = class {
4291
4491
  get latestMemo() {
4292
4492
  return this.memos[this.memos.length - 1];
4293
4493
  }
4294
- async getDeliverable() {
4295
- if (!this._deliverable) {
4494
+ getDeliverable() {
4495
+ const deliverableMemo = this.memos.find(
4496
+ (m) => m.nextPhase === 4 /* COMPLETED */
4497
+ );
4498
+ if (!deliverableMemo) {
4296
4499
  return null;
4297
4500
  }
4298
- if (typeof this._deliverable !== "string") {
4299
- return this._deliverable;
4300
- }
4301
- const regex = /api\/memo-contents\/([0-9]+)$/;
4302
- const result = this._deliverable?.match(regex);
4303
- if (!result) {
4304
- return this._deliverable;
4305
- }
4306
- const deliverable = await this.acpClient.getMemoContent(this._deliverable);
4307
- return tryParseJson(deliverable) || deliverable;
4501
+ return tryParseJson(deliverableMemo.content) || deliverableMemo.content;
4308
4502
  }
4309
4503
  async createRequirement(content) {
4310
4504
  const operations = [];
4505
+ let finalContent = content;
4506
+ if (this.isPrivate) {
4507
+ const memoContent = await this.acpClient.createMemoContent(
4508
+ this.id,
4509
+ content
4510
+ );
4511
+ finalContent = memoContent.url;
4512
+ }
4311
4513
  operations.push(
4312
4514
  this.acpContractClient.createMemo(
4313
4515
  this.id,
4314
- content,
4516
+ finalContent,
4517
+ this.isPrivate ? 4 /* OBJECT_URL */ : 0 /* MESSAGE */,
4518
+ true,
4519
+ 2 /* TRANSACTION */
4520
+ )
4521
+ );
4522
+ return await this.acpContractClient.handleOperation(operations);
4523
+ }
4524
+ async acceptRequirement(memo, reason) {
4525
+ const operations = [];
4526
+ operations.push(
4527
+ this.acpContractClient.signMemo(
4528
+ memo.id,
4529
+ true,
4530
+ reason ?? "Requirement accepted"
4531
+ )
4532
+ );
4533
+ operations.push(
4534
+ this.acpContractClient.createMemo(
4535
+ this.id,
4536
+ reason ?? "Proceeding to delivery",
4315
4537
  0 /* MESSAGE */,
4316
4538
  true,
4317
4539
  2 /* TRANSACTION */
@@ -4319,7 +4541,38 @@ var AcpJob = class {
4319
4541
  );
4320
4542
  return await this.acpContractClient.handleOperation(operations);
4321
4543
  }
4322
- async createPayableRequirement(content, type, amount, recipient, expiredAt = new Date(Date.now() + 1e3 * 60 * 5)) {
4544
+ async createPayableRequirement(content, type, amount, recipient, expiredAt = new Date(Date.now() + 1e3 * 60 * 5), duration) {
4545
+ if (type === 11 /* PAYABLE_REQUEST_SUBSCRIPTION */ && !duration) {
4546
+ throw new acpError_default("Duration is required for subscription payment requests");
4547
+ }
4548
+ if (type === 11 /* PAYABLE_REQUEST_SUBSCRIPTION */) {
4549
+ this.priceType = "subscription" /* SUBSCRIPTION */;
4550
+ if (this.priceValue !== 0) {
4551
+ throw new acpError_default(
4552
+ `Subscription payment request zero budget, got: ${this.priceValue}`
4553
+ );
4554
+ }
4555
+ let parsed;
4556
+ try {
4557
+ const result = JSON.parse(content);
4558
+ if (typeof result !== "object" || result === null || Array.isArray(result)) {
4559
+ throw new Error();
4560
+ }
4561
+ parsed = result;
4562
+ } catch {
4563
+ throw new acpError_default(
4564
+ `Subscription memo content must be a JSON object string, got: ${content}`
4565
+ );
4566
+ }
4567
+ const missing = ["name", "duration", "price"].filter(
4568
+ (k) => !(k in parsed)
4569
+ );
4570
+ if (missing.length > 0) {
4571
+ throw new acpError_default(
4572
+ `Subscription memo content is missing required fields: ${missing.join(", ")}`
4573
+ );
4574
+ }
4575
+ }
4323
4576
  const operations = [];
4324
4577
  if (type === 8 /* PAYABLE_TRANSFER_ESCROW */) {
4325
4578
  operations.push(
@@ -4331,11 +4584,34 @@ var AcpJob = class {
4331
4584
  }
4332
4585
  const feeAmount = new FareAmount(0, this.acpContractClient.config.baseFare);
4333
4586
  const isPercentagePricing = this.priceType === "percentage" /* PERCENTAGE */;
4334
- if (amount.fare.chainId && amount.fare.chainId !== this.acpContractClient.config.chain.id) {
4587
+ let finalContent = content;
4588
+ if (this.isPrivate) {
4589
+ const memoContent = await this.acpClient.createMemoContent(
4590
+ this.id,
4591
+ content
4592
+ );
4593
+ finalContent = memoContent.url;
4594
+ }
4595
+ if (type === 11 /* PAYABLE_REQUEST_SUBSCRIPTION */) {
4335
4596
  operations.push(
4336
- this.acpContractClient.createCrossChainPayableMemo(
4597
+ this.acpContractClient.createSubscriptionMemo(
4337
4598
  this.id,
4338
4599
  content,
4600
+ amount.amount,
4601
+ recipient,
4602
+ isPercentagePricing ? BigInt(Math.round(this.priceValue * 1e4)) : feeAmount.amount,
4603
+ isPercentagePricing ? 3 /* PERCENTAGE_FEE */ : 0 /* NO_FEE */,
4604
+ duration,
4605
+ 2 /* TRANSACTION */,
4606
+ expiredAt,
4607
+ amount.fare.contractAddress
4608
+ )
4609
+ );
4610
+ } else if (amount.fare.chainId && amount.fare.chainId !== this.acpContractClient.config.chain.id) {
4611
+ operations.push(
4612
+ this.acpContractClient.createCrossChainPayableMemo(
4613
+ this.id,
4614
+ finalContent,
4339
4615
  amount.fare.contractAddress,
4340
4616
  amount.amount,
4341
4617
  recipient,
@@ -4351,7 +4627,7 @@ var AcpJob = class {
4351
4627
  operations.push(
4352
4628
  this.acpContractClient.createPayableMemo(
4353
4629
  this.id,
4354
- content,
4630
+ finalContent,
4355
4631
  amount.amount,
4356
4632
  recipient,
4357
4633
  isPercentagePricing ? BigInt(Math.round(this.priceValue * 1e4)) : feeAmount.amount,
@@ -4382,14 +4658,15 @@ var AcpJob = class {
4382
4658
  memo.payableDetails.token,
4383
4659
  this.config
4384
4660
  ) : new FareAmount(0, this.baseFare);
4385
- const totalAmount = baseFareAmount.fare.contractAddress === transferAmount.fare.contractAddress ? baseFareAmount.add(transferAmount) : baseFareAmount;
4661
+ const sameToken = baseFareAmount.fare.contractAddress.toLowerCase() === transferAmount.fare.contractAddress.toLowerCase();
4662
+ const totalAmount = sameToken ? baseFareAmount.add(transferAmount) : baseFareAmount;
4386
4663
  operations.push(
4387
4664
  this.acpContractClient.approveAllowance(
4388
4665
  totalAmount.amount,
4389
4666
  this.baseFare.contractAddress
4390
4667
  )
4391
4668
  );
4392
- if (baseFareAmount.fare.contractAddress !== transferAmount.fare.contractAddress) {
4669
+ if (!sameToken) {
4393
4670
  operations.push(
4394
4671
  this.acpContractClient.approveAllowance(
4395
4672
  transferAmount.amount,
@@ -4398,11 +4675,19 @@ var AcpJob = class {
4398
4675
  );
4399
4676
  }
4400
4677
  operations.push(this.acpContractClient.signMemo(memo.id, true, reason));
4678
+ let finalContent = `Payment made. ${reason ?? ""}`.trim();
4679
+ if (this.isPrivate) {
4680
+ const memoContent = await this.acpClient.createMemoContent(
4681
+ this.id,
4682
+ finalContent
4683
+ );
4684
+ finalContent = memoContent.url;
4685
+ }
4401
4686
  operations.push(
4402
4687
  this.acpContractClient.createMemo(
4403
4688
  this.id,
4404
- `Payment made. ${reason ?? ""}`.trim(),
4405
- 0 /* MESSAGE */,
4689
+ finalContent,
4690
+ this.isPrivate ? 4 /* OBJECT_URL */ : 0 /* MESSAGE */,
4406
4691
  true,
4407
4692
  3 /* EVALUATION */
4408
4693
  )
@@ -4487,12 +4772,20 @@ var AcpJob = class {
4487
4772
  }
4488
4773
  return await latestMemo.sign(false, memoContent);
4489
4774
  }
4775
+ let finalContent = memoContent;
4776
+ if (this.isPrivate) {
4777
+ const memoContent2 = await this.acpClient.createMemoContent(
4778
+ this.id,
4779
+ finalContent
4780
+ );
4781
+ finalContent = memoContent2.url;
4782
+ }
4490
4783
  const operations = [];
4491
4784
  operations.push(
4492
4785
  this.acpContractClient.createMemo(
4493
4786
  this.id,
4494
- memoContent,
4495
- 0 /* MESSAGE */,
4787
+ finalContent,
4788
+ this.isPrivate ? 4 /* OBJECT_URL */ : 0 /* MESSAGE */,
4496
4789
  true,
4497
4790
  5 /* REJECTED */
4498
4791
  )
@@ -4509,10 +4802,18 @@ var AcpJob = class {
4509
4802
  amount.fare.contractAddress
4510
4803
  )
4511
4804
  );
4805
+ let finalContent = memoContent;
4806
+ if (this.isPrivate) {
4807
+ const memoContent2 = await this.acpClient.createMemoContent(
4808
+ this.id,
4809
+ finalContent
4810
+ );
4811
+ finalContent = memoContent2.url;
4812
+ }
4512
4813
  operations.push(
4513
4814
  this.acpContractClient.createPayableMemo(
4514
4815
  this.id,
4515
- memoContent,
4816
+ finalContent,
4516
4817
  amount.amount,
4517
4818
  this.clientAddress,
4518
4819
  feeAmount.amount,
@@ -4583,19 +4884,70 @@ var AcpJob = class {
4583
4884
  );
4584
4885
  return await this.acpContractClient.handleOperation(operations);
4585
4886
  }
4887
+ async paySubscription(reason) {
4888
+ if (this.phase === 6 /* EXPIRED */) {
4889
+ throw new acpError_default("Job has expired, cannot process subscription payment");
4890
+ }
4891
+ if (this.phase === 4 /* COMPLETED */) {
4892
+ throw new acpError_default(
4893
+ "Job is already completed, cannot process subscription payment"
4894
+ );
4895
+ }
4896
+ const memo = this.memos.find(
4897
+ (m) => m.type === 11 /* PAYABLE_REQUEST_SUBSCRIPTION */
4898
+ );
4899
+ if (!memo) {
4900
+ throw new acpError_default("No subscription payment request memo found");
4901
+ }
4902
+ if (!memo.payableDetails) {
4903
+ throw new acpError_default("Subscription memo has no payable details");
4904
+ }
4905
+ const operations = [];
4906
+ operations.push(
4907
+ this.acpContractClient.approveAllowance(
4908
+ memo.payableDetails.amount,
4909
+ memo.payableDetails.token
4910
+ )
4911
+ );
4912
+ operations.push(
4913
+ this.acpContractClient.signMemo(
4914
+ memo.id,
4915
+ true,
4916
+ reason || "Subscription payment approved"
4917
+ )
4918
+ );
4919
+ operations.push(
4920
+ this.acpContractClient.createMemo(
4921
+ this.id,
4922
+ `Subscription payment made. ${reason ?? ""}`.trim(),
4923
+ 0 /* MESSAGE */,
4924
+ true,
4925
+ memo.nextPhase
4926
+ )
4927
+ );
4928
+ return await this.acpContractClient.handleOperation(operations);
4929
+ }
4586
4930
  async evaluate(accept, reason) {
4587
4931
  if (this.latestMemo?.nextPhase !== 4 /* COMPLETED */) {
4588
4932
  throw new acpError_default("No evaluation memo found");
4589
4933
  }
4590
4934
  const memo = this.latestMemo;
4591
- await memo.sign(accept, reason);
4935
+ return await memo.sign(accept, reason);
4592
4936
  }
4593
4937
  async createNotification(content) {
4594
4938
  const operations = [];
4939
+ let finalContent = content;
4940
+ if (this.isPrivate) {
4941
+ const memoContent = await this.acpClient.createMemoContent(
4942
+ this.id,
4943
+ content
4944
+ );
4945
+ finalContent = memoContent.url;
4946
+ }
4595
4947
  operations.push(
4596
4948
  this.acpContractClient.createMemo(
4597
4949
  this.id,
4598
- content,
4950
+ finalContent,
4599
4951
  9 /* NOTIFICATION */,
4600
4952
  true,
4601
4953
  4 /* COMPLETED */
@@ -4613,10 +4965,18 @@ var AcpJob = class {
4613
4965
  );
4614
4966
  const feeAmount = new FareAmount(0, this.acpContractClient.config.baseFare);
4615
4967
  const isPercentagePricing = this.priceType === "percentage" /* PERCENTAGE */ && !skipFee;
4968
+ let finalContent = content;
4969
+ if (this.isPrivate) {
4970
+ const memoContent = await this.acpClient.createMemoContent(
4971
+ this.id,
4972
+ content
4973
+ );
4974
+ finalContent = memoContent.url;
4975
+ }
4616
4976
  operations.push(
4617
4977
  this.acpContractClient.createPayableMemo(
4618
4978
  this.id,
4619
- content,
4979
+ finalContent,
4620
4980
  amount.amount,
4621
4981
  this.clientAddress,
4622
4982
  isPercentagePricing ? BigInt(Math.round(this.priceValue * 1e4)) : feeAmount.amount,
@@ -4723,9 +5083,17 @@ var AcpJob = class {
4723
5083
  );
4724
5084
  const feeAmount = new FareAmount(0, this.acpContractClient.config.baseFare);
4725
5085
  const isPercentagePricing = this.priceType === "percentage" /* PERCENTAGE */ && !skipFee;
5086
+ let finalContent = content;
5087
+ if (this.isPrivate) {
5088
+ const memoContent = await this.acpClient.createMemoContent(
5089
+ this.id,
5090
+ content
5091
+ );
5092
+ finalContent = memoContent.url;
5093
+ }
4726
5094
  const createMemoOperation = this.acpContractClient.createCrossChainPayableMemo(
4727
5095
  this.id,
4728
- content,
5096
+ finalContent,
4729
5097
  amount.fare.contractAddress,
4730
5098
  amount.amount,
4731
5099
  recipient,
@@ -4760,9 +5128,9 @@ var acpJob_default = AcpJob;
4760
5128
 
4761
5129
  // src/acpMemo.ts
4762
5130
  var import_util = __toESM(require("util"));
4763
- var AcpMemo = class {
4764
- constructor(contractClient, id, type, content, nextPhase, status, senderAddress, signedReason, expiry, payableDetails, txHash, signedTxHash, state) {
4765
- this.contractClient = contractClient;
5131
+ var AcpMemo = class _AcpMemo {
5132
+ constructor(acpClient, id, type, content, nextPhase, status, senderAddress, signedReason, expiry, payableDetails, txHash, signedTxHash, state) {
5133
+ this.acpClient = acpClient;
4766
5134
  this.id = id;
4767
5135
  this.type = type;
4768
5136
  this.content = content;
@@ -4780,8 +5148,39 @@ var AcpMemo = class {
4780
5148
  this.payableDetails.feeAmount = BigInt(this.payableDetails.feeAmount);
4781
5149
  }
4782
5150
  }
5151
+ static async build(acpClient, id, type, content, nextPhase, status, senderAddress, signedReason, expiry, payableDetails, txHash, signedTxHash, state) {
5152
+ let memoContent = content;
5153
+ const regex = /api\/memo-contents\/([0-9]+)$/;
5154
+ const result = memoContent.match(regex);
5155
+ if (result) {
5156
+ memoContent = await acpClient.getMemoContent(content);
5157
+ }
5158
+ return new _AcpMemo(
5159
+ acpClient,
5160
+ id,
5161
+ type,
5162
+ memoContent,
5163
+ nextPhase,
5164
+ status,
5165
+ senderAddress,
5166
+ signedReason,
5167
+ expiry,
5168
+ payableDetails,
5169
+ txHash,
5170
+ signedTxHash,
5171
+ state
5172
+ );
5173
+ }
5174
+ async getContent() {
5175
+ const regex = /api\/memo-contents\/([0-9]+)$/;
5176
+ const result = this.content.match(regex);
5177
+ if (!result) {
5178
+ return this.content;
5179
+ }
5180
+ return this.acpClient.getMemoContent(this.content);
5181
+ }
4783
5182
  async create(jobId, isSecured = true) {
4784
- return this.contractClient.createMemo(
5183
+ return this.acpClient.acpContractClient.createMemo(
4785
5184
  jobId,
4786
5185
  this.content,
4787
5186
  this.type,
@@ -4790,44 +5189,30 @@ var AcpMemo = class {
4790
5189
  );
4791
5190
  }
4792
5191
  async sign(approved, reason) {
4793
- const payload = this.contractClient.signMemo(this.id, approved, reason);
4794
- return await this.contractClient.handleOperation([payload]);
4795
- }
4796
- [import_util.default.inspect.custom]() {
4797
- return {
4798
- id: this.id,
4799
- senderAddress: this.senderAddress,
4800
- type: MemoType[this.type],
4801
- status: this.status,
4802
- content: this.content,
4803
- signedReason: this.signedReason,
4804
- txHash: this.txHash,
4805
- signedTxHash: this.signedTxHash,
4806
- nextPhase: AcpJobPhases[this.nextPhase],
4807
- expiry: this.expiry,
4808
- payableDetails: this.payableDetails
4809
- };
4810
- }
4811
- };
4812
- var acpMemo_default = AcpMemo;
4813
-
4814
- // src/acpAccount.ts
4815
- var AcpAccount = class {
4816
- constructor(contractClient, id, clientAddress, providerAddress, metadata) {
4817
- this.contractClient = contractClient;
4818
- this.id = id;
4819
- this.clientAddress = clientAddress;
4820
- this.providerAddress = providerAddress;
4821
- this.metadata = metadata;
4822
- }
4823
- async updateMetadata(metadata) {
4824
- const hash = await this.contractClient.updateAccountMetadata(
5192
+ const payload = this.acpClient.acpContractClient.signMemo(
4825
5193
  this.id,
4826
- JSON.stringify(metadata)
5194
+ approved,
5195
+ reason
4827
5196
  );
4828
- return hash;
5197
+ return await this.acpClient.acpContractClient.handleOperation([payload]);
5198
+ }
5199
+ [import_util.default.inspect.custom]() {
5200
+ return {
5201
+ id: this.id,
5202
+ senderAddress: this.senderAddress,
5203
+ type: MemoType[this.type],
5204
+ status: this.status,
5205
+ content: this.content,
5206
+ signedReason: this.signedReason,
5207
+ txHash: this.txHash,
5208
+ signedTxHash: this.signedTxHash,
5209
+ nextPhase: AcpJobPhases[this.nextPhase],
5210
+ expiry: this.expiry,
5211
+ payableDetails: this.payableDetails
5212
+ };
4829
5213
  }
4830
5214
  };
5215
+ var acpMemo_default = AcpMemo;
4831
5216
 
4832
5217
  // src/acpClient.ts
4833
5218
  var import_axios = __toESM(require("axios"));
@@ -4844,6 +5229,7 @@ var AcpAgent = class {
4844
5229
  this.twitterHandle = args.twitterHandle;
4845
5230
  this.metrics = args.metrics;
4846
5231
  this.resources = args.resources;
5232
+ this.subscriptions = Object.freeze([...args.subscriptions ?? []]);
4847
5233
  }
4848
5234
  };
4849
5235
  var acpAgent_default = AcpAgent;
@@ -4954,6 +5340,7 @@ var AcpClient = class {
4954
5340
  });
4955
5341
  return response.data.data;
4956
5342
  } catch (err) {
5343
+ console.log("err->>", err.response.data);
4957
5344
  throw new acpError_default("Failed to verify auth challenge", err);
4958
5345
  }
4959
5346
  }
@@ -5008,18 +5395,26 @@ var AcpClient = class {
5008
5395
  socket.on("onEvaluate" /* ON_EVALUATE */, async (data, callback) => {
5009
5396
  callback(true);
5010
5397
  if (this.onEvaluate) {
5011
- const job = this._hydrateJob(data);
5398
+ const job = await this._hydrateJob(data);
5012
5399
  this.onEvaluate(job);
5013
5400
  }
5014
5401
  });
5015
5402
  socket.on("onNewTask" /* ON_NEW_TASK */, async (data, callback) => {
5016
5403
  callback(true);
5017
5404
  if (this.onNewTask) {
5018
- const job = this._hydrateJob(data);
5019
- this.onNewTask(
5020
- job,
5021
- job.memos.find((m) => m.id == data.memoToSign)
5022
- );
5405
+ const job = await this._hydrateJob(data);
5406
+ if (job.phase === 6 /* EXPIRED */) {
5407
+ console.warn(`onNewTask skipped for job ${data.id}: job has expired`);
5408
+ return;
5409
+ }
5410
+ try {
5411
+ await this.onNewTask(
5412
+ job,
5413
+ job.memos.find((m) => m.id == data.memoToSign)
5414
+ );
5415
+ } catch (err) {
5416
+ console.error(`onNewTask error for job ${data.id}:`, err);
5417
+ }
5023
5418
  }
5024
5419
  });
5025
5420
  const cleanup = async () => {
@@ -5046,8 +5441,6 @@ var AcpClient = class {
5046
5441
  errCallback(err);
5047
5442
  } else if (err.response?.data.error?.message) {
5048
5443
  throw new acpError_default(err.response?.data.error.message);
5049
- } else {
5050
- throw new acpError_default(`Failed to fetch ${url}: ${err.message}`, err);
5051
5444
  }
5052
5445
  } else {
5053
5446
  throw new acpError_default(
@@ -5057,10 +5450,10 @@ var AcpClient = class {
5057
5450
  }
5058
5451
  }
5059
5452
  }
5060
- _hydrateMemo(memo, contractClient) {
5453
+ async _hydrateMemo(memo) {
5061
5454
  try {
5062
- return new acpMemo_default(
5063
- contractClient,
5455
+ return await acpMemo_default.build(
5456
+ this,
5064
5457
  memo.id,
5065
5458
  memo.memoType,
5066
5459
  memo.content,
@@ -5068,7 +5461,7 @@ var AcpClient = class {
5068
5461
  memo.status,
5069
5462
  memo.senderAddress,
5070
5463
  memo.signedReason,
5071
- memo.expiry ? new Date(Number(memo.expiry) * 1e3) : void 0,
5464
+ memo.expiry ? new Date(parseInt(memo.expiry) * 1e3) : void 0,
5072
5465
  memo.payableDetails,
5073
5466
  memo.txHash,
5074
5467
  memo.signedTxHash,
@@ -5078,7 +5471,7 @@ var AcpClient = class {
5078
5471
  throw new acpError_default(`Failed to hydrate memo ${memo.id}`, err);
5079
5472
  }
5080
5473
  }
5081
- _hydrateJob(job) {
5474
+ async _hydrateJob(job) {
5082
5475
  try {
5083
5476
  return new acpJob_default(
5084
5477
  this,
@@ -5088,31 +5481,27 @@ var AcpClient = class {
5088
5481
  job.evaluatorAddress,
5089
5482
  job.price,
5090
5483
  job.priceTokenAddress,
5091
- job.memos.map(
5092
- (memo) => this._hydrateMemo(
5093
- memo,
5094
- this.contractClientByAddress(job.contractAddress)
5095
- )
5096
- ),
5484
+ await Promise.all(job.memos.map((memo) => this._hydrateMemo(memo))),
5097
5485
  job.phase,
5098
5486
  job.context,
5099
5487
  job.contractAddress,
5100
- job.deliverable,
5101
5488
  job.netPayableAmount
5102
5489
  );
5103
5490
  } catch (err) {
5104
5491
  throw new acpError_default(`Failed to hydrate job ${job.id}`, err);
5105
5492
  }
5106
5493
  }
5107
- _hydrateJobs(rawJobs, options) {
5108
- const jobs = rawJobs.map((job) => {
5109
- try {
5110
- return this._hydrateJob(job);
5111
- } catch (err) {
5112
- console.warn(`${options?.logPrefix ?? "Skipped"}`, err);
5113
- return null;
5114
- }
5115
- });
5494
+ async _hydrateJobs(rawJobs, options) {
5495
+ const jobs = await Promise.all(
5496
+ rawJobs.map((job) => {
5497
+ try {
5498
+ return this._hydrateJob(job);
5499
+ } catch (err) {
5500
+ console.warn(`${options?.logPrefix ?? "Skipped"}`, err);
5501
+ return null;
5502
+ }
5503
+ })
5504
+ );
5116
5505
  return jobs.filter((job) => !!job);
5117
5506
  }
5118
5507
  _hydrateAgent(agent) {
@@ -5141,14 +5530,17 @@ var AcpClient = class {
5141
5530
  offering.requiredFunds,
5142
5531
  offering.slaMinutes,
5143
5532
  offering.requirement,
5144
- offering.deliverable
5533
+ offering.deliverable,
5534
+ offering.subscriptionTiers ?? [],
5535
+ offering.isPrivate
5145
5536
  );
5146
5537
  }),
5147
5538
  contractAddress: agent.contractAddress,
5148
5539
  twitterHandle: agent.twitterHandle,
5149
5540
  walletAddress: agent.walletAddress,
5150
5541
  metrics: agent.metrics,
5151
- resources: agent.resources
5542
+ resources: agent.resources,
5543
+ subscriptions: agent.subscriptions ?? []
5152
5544
  });
5153
5545
  }
5154
5546
  async browseAgents(keyword, options = {}) {
@@ -5197,65 +5589,87 @@ var AcpClient = class {
5197
5589
  return this._hydrateAgent(agent);
5198
5590
  });
5199
5591
  }
5200
- async initiateJob(providerAddress, serviceRequirement, fareAmount, evaluatorAddress, expiredAt = new Date(Date.now() + 1e3 * 60 * 60 * 24)) {
5592
+ async initiateJob(providerAddress, serviceRequirement, fareAmount, evaluatorAddress, expiredAt = new Date(Date.now() + 1e3 * 60 * 60 * 24), offeringName, preferredSubscriptionTier) {
5201
5593
  if (providerAddress === this.walletAddress) {
5202
5594
  throw new acpError_default(
5203
5595
  "Provider address cannot be the same as the client address"
5204
5596
  );
5205
5597
  }
5206
- const account = await this.getByClientAndProvider(
5207
- this.walletAddress,
5598
+ const subscriptionRequired = preferredSubscriptionTier != null;
5599
+ const { account } = await this._resolveSubscriptionAccount(
5208
5600
  providerAddress,
5209
- this.acpContractClient
5601
+ offeringName,
5602
+ preferredSubscriptionTier
5210
5603
  );
5604
+ const budget = subscriptionRequired ? 0n : fareAmount.amount;
5605
+ const subscriptionMetadata = subscriptionRequired ? JSON.stringify({ name: preferredSubscriptionTier ?? "" }) : "";
5211
5606
  const isV1 = [
5212
5607
  baseSepoliaAcpConfig.contractAddress,
5213
5608
  baseSepoliaAcpX402Config.contractAddress,
5214
5609
  baseAcpConfig.contractAddress,
5215
5610
  baseAcpX402Config.contractAddress
5216
5611
  ].includes(this.acpContractClient.config.contractAddress);
5217
- const defaultEvaluatorAddress = isV1 && !evaluatorAddress ? this.walletAddress : import_viem6.zeroAddress;
5612
+ const resolvedEvaluator = evaluatorAddress || (isV1 ? this.walletAddress : import_viem6.zeroAddress);
5218
5613
  const chainId = this.acpContractClient.config.chain.id;
5219
- const isUsdcPaymentToken = USDC_TOKEN_ADDRESS[chainId].toLowerCase() === fareAmount.fare.contractAddress.toLowerCase();
5220
- const isX402Job = this.acpContractClient.config.x402Config && isUsdcPaymentToken;
5221
- const createJobPayload = isV1 || !account ? this.acpContractClient.createJob(
5222
- providerAddress,
5223
- evaluatorAddress || defaultEvaluatorAddress,
5224
- expiredAt,
5225
- fareAmount.fare.contractAddress,
5226
- fareAmount.amount,
5227
- "",
5228
- isX402Job
5229
- ) : this.acpContractClient.createJobWithAccount(
5230
- account.id,
5231
- evaluatorAddress || defaultEvaluatorAddress,
5232
- fareAmount.amount,
5233
- fareAmount.fare.contractAddress,
5234
- expiredAt,
5235
- isX402Job
5236
- );
5237
- const { userOpHash } = await this.acpContractClient.handleOperation([
5238
- createJobPayload
5239
- ]);
5614
+ const isX402Job = this.acpContractClient.config.x402Config && USDC_TOKEN_ADDRESS[chainId].toLowerCase() === fareAmount.fare.contractAddress.toLowerCase();
5615
+ const createJobOperations = [];
5616
+ if (isV1 || !account) {
5617
+ createJobOperations.push(
5618
+ this.acpContractClient.createJob(
5619
+ providerAddress,
5620
+ resolvedEvaluator,
5621
+ expiredAt,
5622
+ fareAmount.fare.contractAddress,
5623
+ budget,
5624
+ subscriptionMetadata,
5625
+ isX402Job
5626
+ )
5627
+ );
5628
+ } else {
5629
+ createJobOperations.push(
5630
+ this.acpContractClient.createJobWithAccount(
5631
+ account.id,
5632
+ resolvedEvaluator,
5633
+ budget,
5634
+ fareAmount.fare.contractAddress,
5635
+ expiredAt,
5636
+ isX402Job
5637
+ )
5638
+ );
5639
+ }
5640
+ const { userOpHash } = await this.acpContractClient.handleOperation(createJobOperations);
5240
5641
  const jobId = await this.acpContractClient.getJobId(
5241
5642
  userOpHash,
5242
5643
  this.walletAddress,
5243
5644
  providerAddress
5244
5645
  );
5245
5646
  const payloads = [];
5246
- const setBudgetWithPaymentTokenPayload = this.acpContractClient.setBudgetWithPaymentToken(
5247
- jobId,
5248
- fareAmount.amount,
5249
- fareAmount.fare.contractAddress
5250
- );
5251
- if (setBudgetWithPaymentTokenPayload) {
5252
- payloads.push(setBudgetWithPaymentTokenPayload);
5647
+ if (!subscriptionRequired) {
5648
+ const setBudgetPayload = this.acpContractClient.setBudgetWithPaymentToken(
5649
+ jobId,
5650
+ fareAmount.amount,
5651
+ fareAmount.fare.contractAddress
5652
+ );
5653
+ if (setBudgetPayload) {
5654
+ payloads.push(setBudgetPayload);
5655
+ }
5656
+ }
5657
+ const memoPayload = subscriptionRequired && typeof serviceRequirement === "object" ? preparePayload({
5658
+ ...serviceRequirement,
5659
+ priceValue: 0,
5660
+ priceType: "subscription" /* SUBSCRIPTION */
5661
+ }) : preparePayload(serviceRequirement);
5662
+ const isPrivate = typeof serviceRequirement === "object" && "isPrivate" in serviceRequirement && serviceRequirement.isPrivate;
5663
+ let content = memoPayload;
5664
+ if (isPrivate) {
5665
+ const memoContent = await this.createMemoContent(jobId, memoPayload);
5666
+ content = memoContent.url;
5253
5667
  }
5254
5668
  payloads.push(
5255
5669
  this.acpContractClient.createMemo(
5256
5670
  jobId,
5257
- preparePayload(serviceRequirement),
5258
- 0 /* MESSAGE */,
5671
+ isPrivate ? content : memoPayload,
5672
+ isPrivate ? 4 /* OBJECT_URL */ : 0 /* MESSAGE */,
5259
5673
  true,
5260
5674
  1 /* NEGOTIATION */
5261
5675
  )
@@ -5313,10 +5727,7 @@ var AcpClient = class {
5313
5727
  if (!memo) {
5314
5728
  return null;
5315
5729
  }
5316
- return this._hydrateMemo(
5317
- memo,
5318
- this.contractClientByAddress(memo.contractAddress)
5319
- );
5730
+ return this._hydrateMemo(memo);
5320
5731
  }
5321
5732
  async getAgent(walletAddress, options = {}) {
5322
5733
  const params = {
@@ -5344,32 +5755,195 @@ var AcpClient = class {
5344
5755
  account.id,
5345
5756
  account.clientAddress,
5346
5757
  account.providerAddress,
5347
- account.metadata
5758
+ account.metadata,
5759
+ account.expiryAt
5348
5760
  );
5349
5761
  }
5350
- async getByClientAndProvider(clientAddress, providerAddress, acpContractClient) {
5351
- const response = await this._fetch(
5352
- `/accounts/client/${clientAddress}/provider/${providerAddress}`,
5353
- "GET",
5354
- {},
5355
- {},
5356
- (err) => {
5357
- if (err.response?.status === 404) {
5358
- return;
5359
- }
5360
- throw new acpError_default("Failed to get account by client and provider", err);
5762
+ /**
5763
+ * Gets account or subscription data for a client–provider pair.
5764
+ * When offeringName is provided, the backend may return subscription tiers and accounts
5765
+ * (ISubscriptionCheckResponse). When not provided, returns a single AcpAccount or null.
5766
+ */
5767
+ async getByClientAndProvider(clientAddress, providerAddress, acpContractClient, offeringName) {
5768
+ let endpoint = `/accounts/client/${clientAddress}/provider/${providerAddress}`;
5769
+ if (offeringName) {
5770
+ endpoint = `/accounts/sub/client/${clientAddress}/provider/${providerAddress}`;
5771
+ }
5772
+ const response = await this._fetch(endpoint, "GET", {}, {}, (err) => {
5773
+ if (err.response?.status === 404) {
5774
+ return;
5361
5775
  }
5362
- );
5776
+ throw new acpError_default("Failed to get account by client and provider", err);
5777
+ });
5363
5778
  if (!response) {
5364
5779
  return null;
5365
5780
  }
5781
+ if (typeof response === "object" && "accounts" in response && Array.isArray(response.accounts)) {
5782
+ const sub = response;
5783
+ sub.accounts = sub.accounts.map((a) => ({
5784
+ ...a,
5785
+ expiryAt: a.expiryAt ?? a.expiry
5786
+ }));
5787
+ return sub;
5788
+ }
5789
+ const account = response;
5790
+ const expiryAt = account.expiryAt ?? account.expiry;
5366
5791
  return new AcpAccount(
5367
5792
  acpContractClient || this.contractClients[0],
5368
- response.id,
5369
- response.clientAddress,
5370
- response.providerAddress,
5371
- response.metadata
5793
+ account.id,
5794
+ account.clientAddress,
5795
+ account.providerAddress,
5796
+ account.metadata,
5797
+ expiryAt
5798
+ );
5799
+ }
5800
+ /**
5801
+ * Narrows a backend response to ISubscriptionCheckResponse if it has an accounts array.
5802
+ */
5803
+ _asSubscriptionCheck(raw) {
5804
+ return raw && typeof raw === "object" && "accounts" in raw ? raw : null;
5805
+ }
5806
+ /**
5807
+ * Resolve the account to use for the job.
5808
+ *
5809
+ * For subscription jobs, priority:
5810
+ * 1. Valid account matching preferred tier
5811
+ * 2. Any valid (non-expired) account
5812
+ * 3. Unactivated account (expiryAt = 0) to reuse
5813
+ * 4. null — createJob will create a new one
5814
+ */
5815
+ async _resolveSubscriptionAccount(providerAddress, offeringName, preferredSubscriptionTier) {
5816
+ if (!offeringName) return { account: null };
5817
+ const raw = await this.getByClientAndProvider(
5818
+ this.walletAddress,
5819
+ providerAddress,
5820
+ this.acpContractClient,
5821
+ offeringName
5822
+ );
5823
+ const subscriptionCheck = raw && typeof raw === "object" && "accounts" in raw ? raw : null;
5824
+ if (!subscriptionCheck) return { account: null };
5825
+ const now = Math.floor(Date.now() / 1e3);
5826
+ const allAccounts = subscriptionCheck.accounts ?? [];
5827
+ 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);
5828
+ if (!matchedAccount) return { account: null };
5829
+ return {
5830
+ account: new AcpAccount(
5831
+ this.acpContractClient,
5832
+ matchedAccount.id,
5833
+ matchedAccount.clientAddress ?? this.walletAddress,
5834
+ matchedAccount.providerAddress ?? providerAddress,
5835
+ matchedAccount.metadata,
5836
+ matchedAccount.expiryAt
5837
+ )
5838
+ };
5839
+ }
5840
+ _findPreferredAccount(accounts, preferredTier, now) {
5841
+ if (!preferredTier) return void 0;
5842
+ return accounts.find((a) => {
5843
+ if (a.expiryAt == null || a.expiryAt <= now) return false;
5844
+ const meta = typeof a.metadata === "string" ? (() => {
5845
+ try {
5846
+ return JSON.parse(a.metadata);
5847
+ } catch {
5848
+ return {};
5849
+ }
5850
+ })() : a.metadata ?? {};
5851
+ return meta?.name === preferredTier;
5852
+ });
5853
+ }
5854
+ /**
5855
+ * Returns the first subscription account with expiryAt > now, or null.
5856
+ */
5857
+ _getValidSubscriptionAccountFromResponse(response, acpContractClient) {
5858
+ const now = Math.floor(Date.now() / 1e3);
5859
+ const valid = response.accounts?.find(
5860
+ (a) => a.expiryAt != null && a.expiryAt > now
5861
+ );
5862
+ if (!valid) return null;
5863
+ return new AcpAccount(
5864
+ acpContractClient,
5865
+ valid.id,
5866
+ valid.clientAddress,
5867
+ valid.providerAddress,
5868
+ valid.metadata,
5869
+ valid.expiryAt
5870
+ );
5871
+ }
5872
+ /**
5873
+ * Seller-facing: determines whether to create a subscription payment request memo.
5874
+ * Call this when handling a new job (e.g. in REQUEST phase); then branch on
5875
+ * needsSubscriptionPayment and use tier when true.
5876
+ */
5877
+ async getSubscriptionPaymentRequirement(clientAddress, providerAddress, offeringName) {
5878
+ let raw;
5879
+ try {
5880
+ raw = await this.getByClientAndProvider(
5881
+ clientAddress,
5882
+ providerAddress,
5883
+ void 0,
5884
+ offeringName
5885
+ );
5886
+ } catch {
5887
+ return {
5888
+ needsSubscriptionPayment: false,
5889
+ action: "no_subscription_required"
5890
+ };
5891
+ }
5892
+ const response = this._asSubscriptionCheck(raw);
5893
+ if (!response?.accounts?.length) {
5894
+ return {
5895
+ needsSubscriptionPayment: false,
5896
+ action: "no_subscription_required"
5897
+ };
5898
+ }
5899
+ const now = Math.floor(Date.now() / 1e3);
5900
+ const hasValidSubscription = response.accounts.some(
5901
+ (a) => a.expiryAt != null && a.expiryAt > now
5372
5902
  );
5903
+ if (hasValidSubscription) {
5904
+ return {
5905
+ needsSubscriptionPayment: false,
5906
+ action: "valid_subscription"
5907
+ };
5908
+ }
5909
+ const firstAccount = response.accounts[0];
5910
+ const tier = {
5911
+ name: firstAccount.metadata?.name ?? "",
5912
+ price: firstAccount.metadata?.price ?? 0,
5913
+ duration: firstAccount.metadata?.duration ?? 0
5914
+ };
5915
+ return {
5916
+ needsSubscriptionPayment: true,
5917
+ tier
5918
+ };
5919
+ }
5920
+ async getValidSubscriptionAccount(providerAddress, offeringName, clientAddress, acpContractClient) {
5921
+ const raw = await this.getByClientAndProvider(
5922
+ clientAddress,
5923
+ providerAddress,
5924
+ acpContractClient,
5925
+ offeringName
5926
+ );
5927
+ const subscriptionCheck = this._asSubscriptionCheck(raw);
5928
+ if (!subscriptionCheck) return null;
5929
+ const contractClient = acpContractClient || this.contractClients[0];
5930
+ const account = this._getValidSubscriptionAccountFromResponse(
5931
+ subscriptionCheck,
5932
+ contractClient
5933
+ );
5934
+ if (account) return account;
5935
+ const legacy = subscriptionCheck;
5936
+ if (legacy.subscriptionRequired && legacy.hasValidSubscription && legacy.account) {
5937
+ return new AcpAccount(
5938
+ contractClient,
5939
+ legacy.account.id,
5940
+ legacy.account.clientAddress,
5941
+ legacy.account.providerAddress,
5942
+ legacy.account.metadata,
5943
+ legacy.account.expiryAt
5944
+ );
5945
+ }
5946
+ return null;
5373
5947
  }
5374
5948
  async createMemoContent(jobId, content) {
5375
5949
  const response = await this._fetch(
@@ -5678,9 +6252,7 @@ var AcpContractClient = class _AcpContractClient extends baseAcpContractClient_d
5678
6252
  throw new acpError_default(`Failed to send user operation`, finalError);
5679
6253
  }
5680
6254
  async getJobId(createJobUserOpHash, clientAddress, providerAddress) {
5681
- const result = await this.sessionKeyClient.getUserOperationReceipt(
5682
- createJobUserOpHash
5683
- );
6255
+ const result = await this.sessionKeyClient.getUserOperationReceipt(createJobUserOpHash);
5684
6256
  if (!result) {
5685
6257
  throw new acpError_default("Failed to get user operation receipt");
5686
6258
  }
@@ -6546,6 +7118,7 @@ var MEMO_MANAGER_ABI = [
6546
7118
  { inputs: [], name: "InvalidMemoState", type: "error" },
6547
7119
  { inputs: [], name: "InvalidMemoStateTransition", type: "error" },
6548
7120
  { inputs: [], name: "InvalidMemoType", type: "error" },
7121
+ { inputs: [], name: "InvalidSubscriptionDuration", type: "error" },
6549
7122
  { inputs: [], name: "JobAlreadyCompleted", type: "error" },
6550
7123
  { inputs: [], name: "JobDoesNotExist", type: "error" },
6551
7124
  { inputs: [], name: "MemoAlreadyApproved", type: "error" },
@@ -6581,6 +7154,7 @@ var MEMO_MANAGER_ABI = [
6581
7154
  { inputs: [], name: "ZeroAddressToken", type: "error" },
6582
7155
  { inputs: [], name: "ZeroAssetManagerAddress", type: "error" },
6583
7156
  { inputs: [], name: "ZeroJobManagerAddress", type: "error" },
7157
+ { inputs: [], name: "ZeroPaymentManagerAddress", type: "error" },
6584
7158
  {
6585
7159
  anonymous: false,
6586
7160
  inputs: [
@@ -6853,6 +7427,31 @@ var MEMO_MANAGER_ABI = [
6853
7427
  name: "RoleRevoked",
6854
7428
  type: "event"
6855
7429
  },
7430
+ {
7431
+ anonymous: false,
7432
+ inputs: [
7433
+ {
7434
+ indexed: true,
7435
+ internalType: "uint256",
7436
+ name: "memoId",
7437
+ type: "uint256"
7438
+ },
7439
+ {
7440
+ indexed: true,
7441
+ internalType: "uint256",
7442
+ name: "accountId",
7443
+ type: "uint256"
7444
+ },
7445
+ {
7446
+ indexed: false,
7447
+ internalType: "uint256",
7448
+ name: "duration",
7449
+ type: "uint256"
7450
+ }
7451
+ ],
7452
+ name: "SubscriptionActivated",
7453
+ type: "event"
7454
+ },
6856
7455
  {
6857
7456
  anonymous: false,
6858
7457
  inputs: [
@@ -6920,17 +7519,6 @@ var MEMO_MANAGER_ABI = [
6920
7519
  stateMutability: "view",
6921
7520
  type: "function"
6922
7521
  },
6923
- {
6924
- inputs: [
6925
- { internalType: "uint256[]", name: "memoIds", type: "uint256[]" },
6926
- { internalType: "bool", name: "approved", type: "bool" },
6927
- { internalType: "string", name: "reason", type: "string" }
6928
- ],
6929
- name: "bulkApproveMemos",
6930
- outputs: [],
6931
- stateMutability: "nonpayable",
6932
- type: "function"
6933
- },
6934
7522
  {
6935
7523
  inputs: [
6936
7524
  { internalType: "uint256", name: "memoId", type: "uint256" },
@@ -7008,9 +7596,40 @@ var MEMO_MANAGER_ABI = [
7008
7596
  type: "function"
7009
7597
  },
7010
7598
  {
7011
- inputs: [{ internalType: "uint256", name: "memoId", type: "uint256" }],
7012
- name: "emergencyApproveMemo",
7013
- outputs: [],
7599
+ inputs: [
7600
+ { internalType: "uint256", name: "jobId", type: "uint256" },
7601
+ { internalType: "address", name: "sender", type: "address" },
7602
+ { internalType: "string", name: "content", type: "string" },
7603
+ {
7604
+ components: [
7605
+ { internalType: "address", name: "token", type: "address" },
7606
+ { internalType: "uint256", name: "amount", type: "uint256" },
7607
+ { internalType: "address", name: "recipient", type: "address" },
7608
+ { internalType: "uint256", name: "feeAmount", type: "uint256" },
7609
+ {
7610
+ internalType: "enum ACPTypes.FeeType",
7611
+ name: "feeType",
7612
+ type: "uint8"
7613
+ },
7614
+ { internalType: "bool", name: "isExecuted", type: "bool" },
7615
+ { internalType: "uint256", name: "expiredAt", type: "uint256" },
7616
+ { internalType: "uint32", name: "lzSrcEid", type: "uint32" },
7617
+ { internalType: "uint32", name: "lzDstEid", type: "uint32" }
7618
+ ],
7619
+ internalType: "struct ACPTypes.PayableDetails",
7620
+ name: "payableDetails_",
7621
+ type: "tuple"
7622
+ },
7623
+ { internalType: "uint256", name: "duration", type: "uint256" },
7624
+ { internalType: "uint256", name: "expiredAt", type: "uint256" },
7625
+ {
7626
+ internalType: "enum ACPTypes.JobPhase",
7627
+ name: "nextPhase",
7628
+ type: "uint8"
7629
+ }
7630
+ ],
7631
+ name: "createSubscriptionMemo",
7632
+ outputs: [{ internalType: "uint256", name: "memoId", type: "uint256" }],
7014
7633
  stateMutability: "nonpayable",
7015
7634
  type: "function"
7016
7635
  },
@@ -7021,13 +7640,6 @@ var MEMO_MANAGER_ABI = [
7021
7640
  stateMutability: "nonpayable",
7022
7641
  type: "function"
7023
7642
  },
7024
- {
7025
- inputs: [],
7026
- name: "getAssetManager",
7027
- outputs: [{ internalType: "address", name: "", type: "address" }],
7028
- stateMutability: "view",
7029
- type: "function"
7030
- },
7031
7643
  {
7032
7644
  inputs: [
7033
7645
  { internalType: "uint256", name: "jobId", type: "uint256" },
@@ -7224,17 +7836,6 @@ var MEMO_MANAGER_ABI = [
7224
7836
  stateMutability: "view",
7225
7837
  type: "function"
7226
7838
  },
7227
- {
7228
- inputs: [{ internalType: "uint256", name: "memoId", type: "uint256" }],
7229
- name: "getMemoApprovalStatus",
7230
- outputs: [
7231
- { internalType: "bool", name: "isApproved", type: "bool" },
7232
- { internalType: "address", name: "approvedBy", type: "address" },
7233
- { internalType: "uint256", name: "approvedAt", type: "uint256" }
7234
- ],
7235
- stateMutability: "view",
7236
- type: "function"
7237
- },
7238
7839
  {
7239
7840
  inputs: [{ internalType: "uint256", name: "memoId", type: "uint256" }],
7240
7841
  name: "getMemoWithPayableDetails",
@@ -7297,34 +7898,6 @@ var MEMO_MANAGER_ABI = [
7297
7898
  stateMutability: "view",
7298
7899
  type: "function"
7299
7900
  },
7300
- {
7301
- inputs: [{ internalType: "uint256", name: "memoId", type: "uint256" }],
7302
- name: "getPayableDetails",
7303
- outputs: [
7304
- {
7305
- components: [
7306
- { internalType: "address", name: "token", type: "address" },
7307
- { internalType: "uint256", name: "amount", type: "uint256" },
7308
- { internalType: "address", name: "recipient", type: "address" },
7309
- { internalType: "uint256", name: "feeAmount", type: "uint256" },
7310
- {
7311
- internalType: "enum ACPTypes.FeeType",
7312
- name: "feeType",
7313
- type: "uint8"
7314
- },
7315
- { internalType: "bool", name: "isExecuted", type: "bool" },
7316
- { internalType: "uint256", name: "expiredAt", type: "uint256" },
7317
- { internalType: "uint32", name: "lzSrcEid", type: "uint32" },
7318
- { internalType: "uint32", name: "lzDstEid", type: "uint32" }
7319
- ],
7320
- internalType: "struct ACPTypes.PayableDetails",
7321
- name: "",
7322
- type: "tuple"
7323
- }
7324
- ],
7325
- stateMutability: "view",
7326
- type: "function"
7327
- },
7328
7901
  {
7329
7902
  inputs: [{ internalType: "bytes32", name: "role", type: "bytes32" }],
7330
7903
  name: "getRoleAdmin",
@@ -7363,16 +7936,6 @@ var MEMO_MANAGER_ABI = [
7363
7936
  stateMutability: "nonpayable",
7364
7937
  type: "function"
7365
7938
  },
7366
- {
7367
- inputs: [
7368
- { internalType: "uint256", name: "jobId", type: "uint256" },
7369
- { internalType: "address", name: "user", type: "address" }
7370
- ],
7371
- name: "isJobEvaluator",
7372
- outputs: [{ internalType: "bool", name: "", type: "bool" }],
7373
- stateMutability: "view",
7374
- type: "function"
7375
- },
7376
7939
  {
7377
7940
  inputs: [
7378
7941
  { internalType: "uint256", name: "memoId", type: "uint256" },
@@ -7544,23 +8107,16 @@ var MEMO_MANAGER_ABI = [
7544
8107
  },
7545
8108
  {
7546
8109
  inputs: [
7547
- {
7548
- internalType: "enum ACPTypes.MemoType",
7549
- name: "memoType",
7550
- type: "uint8"
7551
- },
7552
- { internalType: "uint256", name: "requiredApprovals_", type: "uint256" }
8110
+ { internalType: "address", name: "assetManager_", type: "address" }
7553
8111
  ],
7554
- name: "setApprovalRequirements",
8112
+ name: "setAssetManager",
7555
8113
  outputs: [],
7556
8114
  stateMutability: "nonpayable",
7557
8115
  type: "function"
7558
8116
  },
7559
8117
  {
7560
- inputs: [
7561
- { internalType: "address", name: "assetManager_", type: "address" }
7562
- ],
7563
- name: "setAssetManager",
8118
+ inputs: [{ internalType: "uint256", name: "memoId", type: "uint256" }],
8119
+ name: "setPayableDetailsExecuted",
7564
8120
  outputs: [],
7565
8121
  stateMutability: "nonpayable",
7566
8122
  type: "function"
@@ -7641,12 +8197,14 @@ var MEMO_MANAGER_ABI = [
7641
8197
  var memoManagerAbi_default = MEMO_MANAGER_ABI;
7642
8198
 
7643
8199
  // src/contractClients/acpContractClientV2.ts
8200
+ var import_erc8021 = require("ox/erc8021");
7644
8201
  var AcpContractClientV2 = class _AcpContractClientV2 extends baseAcpContractClient_default {
7645
- constructor(jobManagerAddress, memoManagerAddress, accountManagerAddress, agentWalletAddress, config = baseAcpConfigV2) {
8202
+ constructor(jobManagerAddress, memoManagerAddress, accountManagerAddress, agentWalletAddress, config = baseAcpConfigV2, builderCode) {
7646
8203
  super(agentWalletAddress, config);
7647
8204
  this.jobManagerAddress = jobManagerAddress;
7648
8205
  this.memoManagerAddress = memoManagerAddress;
7649
8206
  this.accountManagerAddress = accountManagerAddress;
8207
+ this.builderCode = builderCode;
7650
8208
  this.RETRY_CONFIG = {
7651
8209
  intervalMs: 200,
7652
8210
  multiplier: 1.1,
@@ -7654,7 +8212,7 @@ var AcpContractClientV2 = class _AcpContractClientV2 extends baseAcpContractClie
7654
8212
  };
7655
8213
  this._sessionKeyClients = {};
7656
8214
  }
7657
- static async build(walletPrivateKey, sessionEntityKeyId, agentWalletAddress, config = baseAcpConfigV2) {
8215
+ static async build(walletPrivateKey, sessionEntityKeyId, agentWalletAddress, config = baseAcpConfigV2, builderCode) {
7658
8216
  const publicClients = {};
7659
8217
  for (const chain of config.chains) {
7660
8218
  publicClients[chain.id] = (0, import_viem9.createPublicClient)({
@@ -7695,7 +8253,8 @@ var AcpContractClientV2 = class _AcpContractClientV2 extends baseAcpContractClie
7695
8253
  memoManagerAddress.result,
7696
8254
  accountManagerAddress.result,
7697
8255
  agentWalletAddress,
7698
- config
8256
+ config,
8257
+ builderCode
7699
8258
  );
7700
8259
  acpContractClient.publicClients = publicClients;
7701
8260
  await acpContractClient.init(walletPrivateKey, sessionEntityKeyId);
@@ -7780,10 +8339,11 @@ var AcpContractClientV2 = class _AcpContractClientV2 extends baseAcpContractClie
7780
8339
  if (!sessionKeyClient) {
7781
8340
  throw new acpError_default("Session key client not initialized");
7782
8341
  }
8342
+ const dataSuffix = this.builderCode ? import_erc8021.Attribution.toDataSuffix({ codes: [this.builderCode] }) : void 0;
7783
8343
  const basePayload = {
7784
8344
  uo: operations.map((operation) => ({
7785
8345
  target: operation.contractAddress,
7786
- data: operation.data,
8346
+ data: dataSuffix ? appendBuilderCodeData(operation.data, dataSuffix) : operation.data,
7787
8347
  value: operation.value
7788
8348
  }))
7789
8349
  };
@@ -7852,6 +8412,37 @@ var AcpContractClientV2 = class _AcpContractClientV2 extends baseAcpContractClie
7852
8412
  }
7853
8413
  return Number(createdJobEvent.args.jobId);
7854
8414
  }
8415
+ async getAccountIdFromUserOpHash(userOpHash) {
8416
+ const result = await this.sessionKeyClient.getUserOperationReceipt(
8417
+ userOpHash,
8418
+ "pending"
8419
+ );
8420
+ if (!result || !result.logs?.length) {
8421
+ return null;
8422
+ }
8423
+ const contractAddresses = [
8424
+ this.contractAddress.toLowerCase(),
8425
+ this.accountManagerAddress.toLowerCase()
8426
+ ];
8427
+ for (const log of result.logs) {
8428
+ if (!contractAddresses.includes(log.address?.toLowerCase())) {
8429
+ continue;
8430
+ }
8431
+ try {
8432
+ const decoded = (0, import_viem9.decodeEventLog)({
8433
+ abi: this.abi,
8434
+ data: log.data,
8435
+ topics: log.topics
8436
+ });
8437
+ if (decoded.eventName === "AccountCreated") {
8438
+ const args = decoded.args;
8439
+ if (args?.accountId != null) return Number(args.accountId);
8440
+ }
8441
+ } catch {
8442
+ }
8443
+ }
8444
+ return null;
8445
+ }
7855
8446
  async updateJobX402Nonce(jobId, nonce) {
7856
8447
  return await this.acpX402.updateJobNonce(jobId, nonce);
7857
8448
  }
@@ -7906,6 +8497,7 @@ var index_default = acpClient_default;
7906
8497
  // Annotate the CommonJS export names for ESM import in node:
7907
8498
  0 && (module.exports = {
7908
8499
  ACP_ABI,
8500
+ AcpAccount,
7909
8501
  AcpAgent,
7910
8502
  AcpAgentSort,
7911
8503
  AcpContractClient,
@@ -7924,6 +8516,7 @@ var index_default = acpClient_default;
7924
8516
  FareAmount,
7925
8517
  FareBigInt,
7926
8518
  MemoType,
8519
+ PriceType,
7927
8520
  baseAcpConfig,
7928
8521
  baseAcpConfigV2,
7929
8522
  baseAcpX402Config,