@zeroxyz/sdk 0.10.0 → 0.12.0

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/CHANGELOG.md CHANGED
@@ -4,6 +4,24 @@ All notable changes to `@zeroxyz/sdk` will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) — with the pre-1.0 caveat that minor versions may include breaking changes.
6
6
 
7
+ ## 0.12.0
8
+
9
+ ### Added
10
+
11
+ - **`exampleRequest` on capability responses** (`capabilities.get()`) — the bare, ready-to-send request body an agent should copy: `.request` unwrapped from the stored `{request,response}` transcript **and** the HTTP-transport envelope, validated against the current `bodySchema`. `null` when there's no usable example or the stored one is stale (missing a now-required field); the raw `example` is retained for the response sample. Prefer `exampleRequest` over `example` when building a fetch body. (ZERO-393, #757)
12
+
13
+ ### Fixed
14
+
15
+ - **MPP session close honors a metered zero receipt** — a no-result metered call now closes the channel at `$0` instead of throwing an orphan `close-failed`. An ambiguous zero close (no explicit metered receipt) still refuses, as before. (#761)
16
+
17
+ ## 0.11.0
18
+
19
+ ### Added
20
+
21
+ - **`client.wallet.import()`** — link an existing self-custody wallet to the session user by private key (`{ privateKey, makePrimary? }` → the linked wallet). 409s when the wallet is already linked to this or another profile. New export `ImportWalletOptions`. (ZERO-478)
22
+ - **`client.wallet.setPrimary()` / `client.wallet.remove()`** — session-mode multi-wallet management: switch the primary payer wallet, or unlink a wallet (rejected for the primary wallet or a non-zero balance). New exports `SetPrimaryWalletOptions` / `RemoveWalletOptions`. (ZERO-478)
23
+ - **`UserWalletDto.importedAt`** — set on wallets linked via `wallet.import()`, absent/null otherwise. (ZERO-478)
24
+
7
25
  ## 0.10.0
8
26
 
9
27
  ### Changed
package/README.md CHANGED
@@ -102,7 +102,7 @@ The SDK refreshes the access token automatically after a `401`, and every refres
102
102
 
103
103
  Endpoints that don't charge pass straight through: a `200` on the first request returns with `payment: null` and `outcome: "success"`. No payment is attempted.
104
104
 
105
- **Building the request — search → get → fetch.** A `search()` result carries the `url` and `method`, so a no-body `GET` capability can go straight to `fetch()`. For anything that takes a request body, call `capabilities.get()` first: its `bodySchema`, `method`, and `headers` are what tell you how to construct the call. Don't guess the body from the search summary.
105
+ **Building the request — search → get → fetch.** A `search()` result carries the `url` and `method`, so a no-body `GET` capability can go straight to `fetch()`. For anything that takes a request body, call `capabilities.get()` first: its `bodySchema`, `method`, and `headers` are what tell you how to construct the call. Don't guess the body from the search summary. When present, **`exampleRequest`** is the single best artifact to copy — the bare, ready-to-send request body from a real successful call, already unwrapped from the stored transcript and validated against the current schema (it's `null` when there's no example or the stored one is stale, so fall back to `bodySchema`). The raw `example` (`{ request, response }`) is retained for the response sample.
106
106
 
107
107
  Each search result also carries a `token` field (format: `z_xxx.N`) — a short server-issued attribution token that encodes the search context and the result's position. Pass it as `capabilityId` to `fetch()` (or as the `id` to `capabilities.get()`) instead of the raw uid/slug; the server uses it to attribute the run back to the originating search for ranking and analytics. Tokens expire when the session rolls, so use them within the same search session.
108
108
 
@@ -223,14 +223,26 @@ const url = await client.wallet.fundingUrl({ amount: "10" });
223
223
 
224
224
  Poll `balance()` to decide when to top up. Then either open `fundingUrl()` yourself to refill the shared wallet — from an ops dashboard, a low-balance alert, or alongside a direct USDC transfer to the `wallet.address()` result — or hand the link to the end user. Either way the link is **single-use** and burns when opened, so generate it at the moment of funding and don't pre-open one you intend to give to someone else.
225
225
 
226
- In **session mode** (acting for a Zero user) the wallet is Zero-managed, and a couple more methods apply:
226
+ In **session mode** (acting for a Zero user) wallets are managed server-side, and a few more methods apply:
227
227
 
228
228
  ```ts
229
229
  await client.wallet.list(); // every wallet linked to the session user
230
230
  await client.wallet.provision(); // create the user's managed wallet (idempotent)
231
+
232
+ // Link an existing self-custody wallet by private key (0x prefix optional).
233
+ // Pass makePrimary to have it pay for the user's calls from now on.
234
+ const imported = await client.wallet.import({
235
+ privateKey: "0x…",
236
+ makePrimary: true,
237
+ });
238
+
239
+ // Switch which linked wallet is primary, or unlink one. remove() rejects the
240
+ // primary wallet and any wallet holding a balance — reassign / drain first.
241
+ await client.wallet.setPrimary({ walletAddress: imported.walletAddress });
242
+ await client.wallet.remove({ walletAddress: "0x…" });
231
243
  ```
232
244
 
233
- `wallet.address()` covers the common case (resolve — or provision — the primary managed wallet); reach for `list()`/`provision()` directly only when you need the full wallet objects or explicit provisioning.
245
+ `wallet.address()` covers the common case (resolve — or provision — the primary managed wallet); reach for the methods above when you need the full wallet objects, explicit provisioning, or multi-wallet management. Imported wallets carry an `importedAt` timestamp on the returned wallet objects.
234
246
 
235
247
  ## Namespaces
236
248
 
@@ -249,7 +261,9 @@ const { capabilities } = await client.search("translate text to french", {
249
261
  const cap = await client.capabilities.get("cap_abc"); // by uid
250
262
  const cap2 = await client.capabilities.get("z_Ab12cd.1"); // by search token
251
263
 
252
- // Wallet — balance + one-time funding URL. See "Wallet & funding" above.
264
+ // Wallet — balance, one-time funding URL, and (session mode) multi-wallet
265
+ // management: list / provision / import / setPrimary / remove. See
266
+ // "Wallet & funding" above.
253
267
  const { amount } = await client.wallet.balance();
254
268
  const fundUrl = await client.wallet.fundingUrl({ amount: "10" });
255
269
 
@@ -589,6 +589,7 @@ var pickSessionCloseAmount = (receipt, openTimeCumulative) => {
589
589
  if (receipt.metered === true) return fromReceipt;
590
590
  return fromReceipt > openTimeCumulative ? fromReceipt : openTimeCumulative;
591
591
  };
592
+ var isMeteredZeroReceipt = (receipt) => receipt?.metered === true && BigInt(receipt.acceptedCumulative) === 0n && BigInt(receipt.spent) === 0n;
592
593
  var raceWithSignal = (p, signal, client) => {
593
594
  if (!signal) return p;
594
595
  if (signal.aborted) {
@@ -1162,7 +1163,7 @@ var Payments = class {
1162
1163
  }
1163
1164
  const receipt = readSessionReceipt(response);
1164
1165
  let closeAmount = pickSessionCloseAmount(receipt, openTimeCumulative);
1165
- if (closeAmount === 0n && channelEntry.cumulativeAmount > 0n) {
1166
+ if (closeAmount === 0n && channelEntry.cumulativeAmount > 0n && !isMeteredZeroReceipt(receipt)) {
1166
1167
  closeAmount = channelEntry.cumulativeAmount;
1167
1168
  }
1168
1169
  const reconcileAmount = receipt && isUintString(receipt.acceptedCumulative) ? receipt.acceptedCumulative : closeAmount.toString();
@@ -1179,7 +1180,7 @@ var Payments = class {
1179
1180
  capturedAmount,
1180
1181
  cause
1181
1182
  });
1182
- if (closeAmount === 0n) {
1183
+ if (closeAmount === 0n && !isMeteredZeroReceipt(receipt)) {
1183
1184
  throw closeFailed(
1184
1185
  `Refusing to sign zero close credential for channel ${channelId} \u2014 neither receipt nor channelEntry produced a nonzero cumulativeAmount. Query escrow directly to recover; check logger for an earlier 'non-bigint cumulativeAmount' warning.`
1185
1186
  );
@@ -1950,7 +1951,7 @@ var clientFetch = async (client, url, opts = {}) => {
1950
1951
 
1951
1952
  // package.json
1952
1953
  var package_default = {
1953
- version: "0.10.0"};
1954
+ version: "0.12.0"};
1954
1955
 
1955
1956
  // src/version.ts
1956
1957
  var SDK_VERSION = package_default.version;
@@ -2441,6 +2442,7 @@ var userWalletDtoSchema = z.object({
2441
2442
  source: z.enum(["self_custody", "privy_embedded"]),
2442
2443
  isPrimary: z.boolean(),
2443
2444
  linkedAt: z.union([z.string(), z.date()]).nullable().optional(),
2445
+ importedAt: z.union([z.string(), z.date()]).nullable().optional(),
2444
2446
  delegationGranted: z.boolean(),
2445
2447
  signerQuorumId: z.string().nullable(),
2446
2448
  // Per-transaction USDC cap in base units (6 dp). null = unlimited, 0 = free-only.
@@ -2839,6 +2841,12 @@ var capabilityResponseSchema = z.object({
2839
2841
  bodySchema: z.record(z.string(), z.unknown()).nullable(),
2840
2842
  responseSchema: z.record(z.string(), z.unknown()).nullable(),
2841
2843
  example: z.object({ request: z.unknown(), response: z.unknown() }).nullable(),
2844
+ // The bare, schema-validated request body to send — `.request` unwrapped
2845
+ // from the transcript envelope and checked against the current bodySchema.
2846
+ // Null when there's no usable example or it's stale vs the schema; `example`
2847
+ // is retained for the response sample. Optional for back-compat with older
2848
+ // servers that don't emit it.
2849
+ exampleRequest: z.record(z.string(), z.unknown()).nullable().optional(),
2842
2850
  tags: z.array(z.string()).nullable(),
2843
2851
  exampleAgentPrompt: z.string().nullable().optional(),
2844
2852
  whatItDoes: z.string().nullable().optional(),
@@ -3226,6 +3234,48 @@ var Wallet = class {
3226
3234
  },
3227
3235
  userWalletDtoSchema
3228
3236
  );
3237
+ // Link an existing self-custody wallet to the session user by private key.
3238
+ // Session-only. 409s when the wallet is already linked (to this or another
3239
+ // profile — the error message says which).
3240
+ import = (opts) => request(
3241
+ this.client,
3242
+ {
3243
+ method: "POST",
3244
+ path: "/v1/users/me/wallets/import",
3245
+ body: {
3246
+ privateKey: opts.privateKey,
3247
+ ...opts.makePrimary !== void 0 ? { makePrimary: opts.makePrimary } : {}
3248
+ },
3249
+ signal: opts.signal
3250
+ },
3251
+ userWalletDtoSchema
3252
+ );
3253
+ // Make one of the session user's linked wallets the primary payer.
3254
+ // Session-only. 404s when the address isn't linked to the user.
3255
+ setPrimary = (opts) => request(
3256
+ this.client,
3257
+ {
3258
+ method: "PATCH",
3259
+ path: "/v1/users/me/wallets/primary",
3260
+ body: { walletAddress: opts.walletAddress },
3261
+ signal: opts.signal
3262
+ },
3263
+ userWalletDtoSchema
3264
+ );
3265
+ // Unlink a wallet from the session user. Session-only. 409s on the primary
3266
+ // wallet or a non-zero balance (the error message says which); 404s when
3267
+ // the address isn't linked. Responds 204 — resolves to void.
3268
+ remove = async (opts) => {
3269
+ await request(
3270
+ this.client,
3271
+ {
3272
+ method: "DELETE",
3273
+ path: `/v1/users/me/wallets/${opts.walletAddress}`,
3274
+ signal: opts.signal
3275
+ },
3276
+ z.undefined()
3277
+ );
3278
+ };
3229
3279
  // Routes by identity: account mode signs EIP-191 against /v1/wallet/fund-url,
3230
3280
  // session mode hits /v1/users/me/fund-url (Bearer-authed, wallet resolved
3231
3281
  // server-side). Both honor `provider` (coinbase | stripe).
@@ -3744,5 +3794,5 @@ var ZeroClient = class _ZeroClient {
3744
3794
  };
3745
3795
 
3746
3796
  export { Auth, AuthAgent, AuthDevice, BUG_REPORT_CATEGORIES, BugReports, Capabilities, DEFAULT_BASE_URL, DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT_MS, FETCH_SKIP_REASONS, FETCH_WARNINGS, Payments, Runs, SDK_VERSION, TEMPO_CHAIN_ID2 as TEMPO_CHAIN_ID, TEMPO_TESTNET_CHAIN_ID, USDC_BASE, USDC_DECIMALS, USDC_TEMPO, Wallet, ZeroAgentAuthError, ZeroApiError, ZeroAuthError, ZeroClient, ZeroConfigurationError, ZeroError, ZeroPaymentError, ZeroSessionCloseFailedError, ZeroTimeoutError, ZeroValidationError, ZeroWalletError, asSchemaNode, coerceTempoChainId, createManagedAccount, extractInputEnvelope, isShortToken, normalizeTransportEnvelopeRequest, paymentHasAnchor, tempoChainLabelFromId, unwrapTransportEnvelope };
3747
- //# sourceMappingURL=chunk-A7WZYQXS.js.map
3748
- //# sourceMappingURL=chunk-A7WZYQXS.js.map
3797
+ //# sourceMappingURL=chunk-KDWESC42.js.map
3798
+ //# sourceMappingURL=chunk-KDWESC42.js.map