@agentkv/client 0.1.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/LICENSE +21 -0
- package/README.md +131 -0
- package/dist/index.cjs +1263 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +584 -0
- package/dist/index.d.ts +584 -0
- package/dist/index.js +1240 -0
- package/dist/index.js.map +1 -0
- package/package.json +76 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/account.ts","../src/crypto.ts","../src/payment.ts","../src/types.ts"],"sourcesContent":["// client/src/index.ts\nexport const VERSION = \"0.1.0\";\n\nimport { fetchWithRetry } from \"@agentx402-ai/core\";\nimport { hexToBytes } from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { isAccountKeyFormat } from \"./account\";\nimport {\n decrypt,\n deriveKeyMaterial,\n encrypt,\n hashKey,\n type KeyMaterial,\n normalizeEncryptionKey,\n} from \"./crypto\";\nimport {\n buildBearerHeaders,\n buildIdentityHeaders,\n buildPaymentHeader,\n challengePriceUsd,\n decodeBase64Utf8,\n freshNonce,\n nonceFromIdempotencyKey,\n} from \"./payment\";\nimport {\n AgentKVError,\n type AgentKVOptions,\n DEFAULT_NETWORK,\n type DeleteResult,\n type DepositResult,\n type GetOptions,\n type OpInlineRequest,\n type OpInlineResponse,\n type SetOptions,\n type SetResult,\n type Signer,\n SpendCapError,\n type TopoffPayerRequest,\n type UsageBlock,\n} from \"./types\";\n\nexport { generateAccountKey, isAccountKeyFormat } from \"./account\";\nexport type { KeyMaterial } from \"./crypto\";\nexport { decrypt, deriveKeyMaterial, encrypt, hashKey } from \"./crypto\";\nexport type {\n AgentKVOptions,\n DeleteResult,\n DepositResult,\n ErrorBody,\n GetOptions,\n OpInlineRequest,\n OpInlineResponse,\n SetOptions,\n SetResult,\n Signer,\n TopoffPayerRequest,\n UsageBlock,\n} from \"./types\";\nexport { AgentKVError, SpendCapError } from \"./types\";\n\n// Additive `/v1` path prefix (the backend registers every route at both its\n// legacy path and this `/v1` sibling, pointing at the SAME handler). The client\n// cannot import the backend's version module across packages, so this literal\n// is kept in sync by the routing tests in `test/paths.test.ts`.\nconst V1 = \"/v1\";\n\n// EIP-712 typed data signed to derive the AES key material in sign-to-derive mode. Unlike a\n// bare personal_sign string (which ANY dapp/relayer can get a user to sign, then reproduce to\n// recover the key), this is DOMAIN-SCOPED: wallets render the \"AgentKV Encryption\" domain, so\n// a generic-text phishing prompt cannot elicit the same signature. The signature IS the\n// complete key material (value + key-name + blind-index MAC), so callers who can't treat it as\n// secret-grade should construct with an explicit `encryptionKey` instead. The domain omits\n// chainId on purpose so the key is stable across networks; changing any field below re-keys all\n// sign-to-derive data.\nconst ENC_DERIVATION_DOMAIN = { name: \"AgentKV Encryption\", version: \"1\" } as const;\nconst ENC_DERIVATION_TYPES = {\n Derive: [\n { name: \"purpose\", type: \"string\" },\n { name: \"version\", type: \"string\" },\n ],\n} as const;\nconst ENC_DERIVATION_MESSAGE = { purpose: \"encryption-key\", version: \"v1\" } as const;\n\n// Credit costs in USD for the spend-cap gate in account-key (bearer) mode.\n// In account mode a set/get debits PREPAID CREDITS server-side (READ_COST=3,\n// WRITE_COST=5 credits; 1 credit = $0.0001), NOT the x402 op price ($0.003/$0.005).\n// We mirror the backend's credit costs so the cap bounds a runaway agent's actual\n// per-op spend. Keep in sync with the backend's credit-cost constants — the\n// separate packages can't share an import, so `pricing.test.ts` pins these to their\n// documented derivation (a parity guard: any drift is a caught, deliberate change).\n/** USD value of one prepaid credit ($1 mints 10,000 credits). */\nexport const CREDIT_VALUE_USD = 0.0001;\nexport const ACCOUNT_READ_USD = 0.0003; // READ_COST=3 credits × $0.0001/credit\nexport const ACCOUNT_WRITE_USD = 0.0005; // WRITE_COST=5 credits × $0.0001/credit\n\n/**\n * Built-in ceiling on a SERVER-QUOTED per-op price when no `maxSpendUsd` is configured.\n * The advertised op price is ~$0.005; without this, a compromised or spoofed worker could\n * answer a routine read with a 402 challenge for the wallet's entire balance and the client\n * would sign the EIP-3009 authorization (the default config has no per-op cap). Callers who\n * legitimately need a pricier op opt in explicitly via `maxSpendUsd`.\n */\nexport const DEFAULT_MAX_OP_USD = 0.05;\n\n/**\n * Convert a USD amount to a whole number of atomic USDC units (1e6), or `null` if it is not\n * a positive whole-atomic amount. Uses a RELATIVE epsilon rather than strict float equality:\n * IEEE-754 makes `33.3 * 1e6 === 33299999.999999996`, so `x*1e6 !== Math.round(x*1e6)` wrongly\n * rejects the exactly-whole-atomic $33.30 / $1.005. The relative test still rejects genuine\n * sub-atomic fractions (e.g. 1.0000005, relative error ~5e-7 ≫ 1e-9).\n */\nfunction toWholeAtomicUsd(amountUsd: number): number | null {\n if (!Number.isFinite(amountUsd)) return null;\n const atomic = Math.round(amountUsd * 1_000_000);\n if (!Number.isInteger(atomic) || atomic <= 0) return null;\n if (Math.abs(amountUsd * 1_000_000 - atomic) > atomic * 1e-9) return null;\n return atomic;\n}\n\nexport class AgentKV {\n /**\n * The signing wallet. `undefined` in account-key mode (a managed account has no\n * wallet that can sign) — the `ak_…` bearer token is the identity instead.\n */\n readonly signer?: Signer;\n /**\n * The wallet address, the per-wallet namespace. In account-key mode there is no\n * wallet, so this is the zero address (a documented sentinel: the account key —\n * not an address — is the identity; the server names storage by the key's hash).\n */\n /** The wallet address (its namespace) in wallet/signer mode; `undefined` in account-key mode. */\n readonly address: `0x${string}` | undefined;\n /** The raw `ak_…` bearer token in account-key mode; `undefined` otherwise. */\n readonly accountKey?: string;\n readonly endpoint: string;\n readonly network: string;\n readonly maxSpendUsd?: number;\n readonly maxSessionSpendUsd?: number;\n /** Bounded internal retries on transient failures (network error / 5xx). */\n readonly maxRetries: number;\n private readonly timeoutMs?: number;\n private readonly fetchImpl?: typeof fetch;\n private _ikm?: Uint8Array;\n private _km?: KeyMaterial;\n private _kmPromise?: Promise<KeyMaterial>;\n private sessionSpentUsd = 0;\n\n // --- Discounted Prepay state (opt-in; undefined => Pay-as-you-go, unchanged) ---\n private readonly prepay?: { watermark: number; topoff: number; async?: boolean };\n /** Top-off amount in atomic USDC units (1e6), computed once in the constructor. */\n private readonly topoffAtomic: number = 0;\n /** Account-key top-off hook (account-key mode only; validated in the constructor). */\n private readonly topoffPayer?: (req: TopoffPayerRequest) => Promise<void>;\n /**\n * Account-key inline x402 transport hook (account-key mode only; validated in\n * the constructor). Mutually exclusive with `topoffPayer` PER OP — `topoffPayer`\n * always takes precedence when both are configured (see the call sites in\n * set()/get(), which gate on `!this.topoffPayer`).\n */\n private readonly opInlinePayer?: (req: OpInlineRequest) => Promise<OpInlineResponse>;\n /** Last-known credit balance as an EXACT integer credit count (never USD floats). */\n private knownCredits?: number;\n /** Synchronous single-flight guard: at most one in-flight top-off at a time. */\n private topoffInFlight = false;\n /**\n * The in-flight SYNCHRONOUS top-off deposit (account mode), published so a concurrent op\n * that hits a hard 402 but can't claim the single-flight can await it and retry.\n */\n private topoffPromise: Promise<void> | undefined;\n /** Cached last `PAYMENT-REQUIRED` header — the template for a proactive single-shot. */\n private challengeTemplate?: string;\n\n constructor(opts: AgentKVOptions) {\n this.endpoint = opts.endpoint.replace(/\\/+$/, \"\");\n this.network = opts.network ?? DEFAULT_NETWORK;\n this.maxSpendUsd = opts.maxSpendUsd;\n this.maxSessionSpendUsd = opts.maxSessionSpendUsd;\n this.maxRetries = Math.max(0, Math.floor(opts.retries ?? 2));\n this.timeoutMs = opts.timeoutMs;\n this.fetchImpl = opts.fetch;\n\n // Step 4a: validate prepay at construction (fail fast, not after a round-trip).\n const isAccountMode = \"accountKey\" in opts && opts.accountKey != null;\n if (opts.topoffPayer !== undefined) {\n if (typeof opts.topoffPayer !== \"function\") {\n throw new AgentKVError(\"topoffPayer must be a function\", \"invalid_config\", 0);\n }\n if (!isAccountMode) {\n // Wallet mode signs its own top-offs (runDeposit); a hook would be silently\n // ignored — reject like every other inert config.\n throw new AgentKVError(\n \"topoffPayer is account-key-mode only; wallet mode pays its own top-offs\",\n \"invalid_config\",\n 0,\n );\n }\n if (!opts.prepay) {\n // The hook only fires through the prepay watermark/402 machinery; without\n // prepay it could never be called.\n throw new AgentKVError(\n \"topoffPayer requires prepay ({ watermark, topoff }) to control when it fires\",\n \"invalid_config\",\n 0,\n );\n }\n this.topoffPayer = opts.topoffPayer;\n }\n if (opts.opInlinePayer !== undefined) {\n if (typeof opts.opInlinePayer !== \"function\") {\n throw new AgentKVError(\"opInlinePayer must be a function\", \"invalid_config\", 0);\n }\n if (!isAccountMode) {\n // Wallet mode signs (and pays) its own x402 challenges directly; a hook\n // would be silently ignored — reject like every other inert config.\n throw new AgentKVError(\n \"opInlinePayer is account-key-mode only; wallet mode pays its own x402 challenges\",\n \"invalid_config\",\n 0,\n );\n }\n // Unlike topoffPayer, opInlinePayer needs no `prepay`: it is pay-per-op,\n // fired directly off a hard 402 with no watermark/top-off machinery.\n this.opInlinePayer = opts.opInlinePayer;\n }\n if (opts.prepay) {\n if (isAccountMode && !opts.topoffPayer) {\n // Without a payer hook every top-off mechanism is unreachable in account-key\n // mode (bearer ops have no signing wallet), so prepay would be silently inert\n // and credits would simply run out. Reject up front.\n throw new AgentKVError(\n \"prepay in account-key mode requires a topoffPayer hook (or fund via fundAccount() / 'agentkv fund')\",\n \"invalid_config\",\n 0,\n );\n }\n // prepay.async IS supported in account-key mode — maybeAsyncTopoff()\n // dispatches through the topoffPayer hook (runAccountTopoff), not runDeposit\n // (which has no signing wallet in account mode). See maybeAsyncTopoff() below.\n const topoffAtomic = toWholeAtomicUsd(opts.prepay.topoff);\n if (topoffAtomic === null || !(opts.prepay.topoff >= 1)) {\n throw new AgentKVError(\n \"prepay.topoff must be >= $1 (a whole number of atomic USDC units)\",\n \"invalid_config\",\n 0,\n );\n }\n if (!(opts.prepay.watermark >= 0)) {\n throw new AgentKVError(\"prepay.watermark must be >= 0\", \"invalid_config\", 0);\n }\n this.prepay = opts.prepay;\n this.topoffAtomic = topoffAtomic;\n }\n\n // Discriminate on the VALUE, not mere key presence: `{ privateKey, accountKey:\n // undefined }` (e.g. from a spread config where accountKey is optional) is WALLET\n // mode, not account mode. `\"accountKey\" in opts` would be true for a present-but-\n // undefined key and wrongly enter account mode (throwing invalid_config).\n if (isAccountMode) {\n // Account-key mode: no signing wallet. The `ak_…` bearer token is the\n // identity. There is no wallet to derive an AES key from, so an explicit\n // `encryptionKey` is REQUIRED and used directly to derive the key material\n // (getKeyMaterial never hits sign-to-derive — there is nothing to sign).\n if (!isAccountKeyFormat(opts.accountKey)) {\n throw new AgentKVError(\n \"accountKey must be a string of the form ak_<64 lowercase hex>\",\n \"invalid_config\",\n 0,\n );\n }\n if (!opts.encryptionKey) {\n throw new AgentKVError(\n \"account-key mode requires an explicit encryptionKey (there is no wallet to derive one from)\",\n \"invalid_config\",\n 0,\n );\n }\n this.accountKey = opts.accountKey;\n this.signer = undefined;\n // No wallet address in account-key mode; the account key (its server-side hash)\n // is the namespace. `address` is `undefined` (honest) — never sent on the wire.\n this.address = undefined;\n this._ikm = normalizeEncryptionKey(opts.encryptionKey);\n } else if (\"privateKey\" in opts && opts.privateKey != null) {\n // Discriminate on the VALUE (not mere presence), mirroring the accountKey guard:\n // `{ ...cfg, privateKey: undefined, signer: validSigner }` must fall through to the\n // signer branch, not enter here and throw a cryptic viem error from\n // privateKeyToAccount(undefined).\n if (\"encryptionKey\" in opts && opts.encryptionKey) {\n // privateKey mode derives the AES key from the wallet key itself; a caller-supplied\n // encryptionKey would be SILENTLY ignored (data encrypted under a different key than\n // they think). Fail fast — use `{ signer, encryptionKey }` for an explicit key.\n throw new AgentKVError(\n \"privateKey mode derives its encryption key from the wallet key; do not also pass \" +\n \"encryptionKey — use { signer, encryptionKey } for an explicit AES key\",\n \"invalid_config\",\n 0,\n );\n }\n this.signer = privateKeyToAccount(opts.privateKey);\n this._ikm = hexToBytes(opts.privateKey); // wallet privkey is the per-wallet HKDF input\n this.address = this.signer.address;\n } else if (\"signer\" in opts && opts.signer != null) {\n this.signer = opts.signer;\n if (\"encryptionKey\" in opts && opts.encryptionKey) {\n this._ikm = normalizeEncryptionKey(opts.encryptionKey);\n }\n // else: lazy sign-to-derive in getKeyMaterial()\n this.address = this.signer.address;\n } else {\n // Reached when every auth key is absent OR present-but-nullish (e.g. `accountKey:\n // undefined`, `privateKey: undefined`, or `signer: undefined` from a spread config).\n // Fail with a clear config error instead of a bare TypeError on `this.signer.address`\n // or a cryptic viem error from privateKeyToAccount(undefined).\n throw new AgentKVError(\n \"invalid auth config: provide one of { privateKey } | { signer } | { accountKey, encryptionKey }\",\n \"invalid_config\",\n 0,\n );\n }\n }\n\n /**\n * Resolve (and memoize) the AES key. Async only for the sign-to-derive shape\n * (`{signer}` with no encryptionKey): the key is `HKDF` over a fixed-message\n * signature, which is stable ONLY for deterministic ECDSA signers (local keys /\n * RFC-6979). Non-deterministic signers (some MPC/threshold backends) would\n * derive a different key each run and fail to decrypt — those must pass an\n * explicit `encryptionKey`.\n */\n private getKeyMaterial(): Promise<KeyMaterial> {\n if (this._km) return Promise.resolve(this._km);\n if (!this._kmPromise) {\n this._kmPromise = (async () => {\n let ikm = this._ikm;\n if (!ikm) {\n // Sign-to-derive: only the `{signer}` (no explicit key) shape reaches\n // here. Account-key mode always has an explicit `_ikm`, so `signer` is\n // guaranteed present on this branch.\n if (!this.signer) {\n throw new AgentKVError(\n \"no encryption key material: account-key mode requires an explicit encryptionKey\",\n \"invalid_config\",\n 0,\n );\n }\n const sig = await this.signer.signTypedData({\n domain: ENC_DERIVATION_DOMAIN,\n types: ENC_DERIVATION_TYPES,\n primaryType: \"Derive\",\n message: ENC_DERIVATION_MESSAGE,\n });\n const sigBytes = hexToBytes(sig);\n // Hash the signature's raw bytes as the HKDF ikm. Require the STANDARD 65-byte ECDSA\n // serialization: a signer that returns a 64-byte EIP-2098 compact form or an\n // ERC-1271/6492 smart-account wrapper blob would derive a DIFFERENT key for the same\n // wallet and silently lose access to prior data. Reject those clearly (they must\n // construct with an explicit encryptionKey). NB: we do NOT normalize the v byte.\n if (sigBytes.length !== 65) {\n throw new AgentKVError(\n `sign-to-derive expected a 65-byte EIP-712 signature but got ${sigBytes.length} bytes; ` +\n \"this signer's format is unstable for key derivation — construct with an explicit encryptionKey\",\n \"invalid_config\",\n 0,\n );\n }\n ikm = sigBytes;\n }\n const km = deriveKeyMaterial(ikm);\n this._km = km;\n return km;\n })().catch((err) => {\n // Do NOT cache a rejected derivation: a transient signTypedData failure (dismissed\n // wallet prompt, MPC/RPC hiccup) must not permanently brick every future op on this\n // instance. Clear the memo so the next call retries the derivation from scratch.\n this._kmPromise = undefined;\n throw err;\n });\n }\n return this._kmPromise;\n }\n\n /**\n * Decrypt a value envelope with the current value key, binding the key's blind-index\n * digest into the AAD so a value the server serves for the wrong key fails the auth tag.\n */\n private async decryptValue(packed: string, key: string): Promise<string> {\n const km = await this.getKeyMaterial();\n // Bind the key's blind-index digest into the AAD so a value the server serves for the\n // WRONG key fails the auth tag instead of silently decrypting (substitution defense).\n return decrypt(km.value, packed, hashKey(km.mac, key));\n }\n\n private assertSpend(usd: number, opts: { bypassPerOpCap?: boolean } = {}): void {\n // Top-offs pass bypassPerOpCap: a credit purchase is not a per-op charge, so\n // the per-call cap (which bounds individual pay-per-op spend) must not gate it\n // — mirroring topoffFitsSessionCap() on the synchronous top-off paths.\n if (!opts.bypassPerOpCap && this.maxSpendUsd !== undefined && usd > this.maxSpendUsd) {\n throw new SpendCapError(`spend $${usd} exceeds per-call cap $${this.maxSpendUsd}`);\n }\n if (\n this.maxSessionSpendUsd !== undefined &&\n this.sessionSpentUsd + usd > this.maxSessionSpendUsd\n ) {\n throw new SpendCapError(\n `spend $${usd} would exceed session cap $${this.maxSessionSpendUsd} (spent $${this.sessionSpentUsd})`,\n );\n }\n }\n\n private recordSpend(usd: number): void {\n this.sessionSpentUsd += usd;\n }\n\n /**\n * Reject a SERVER-QUOTED per-op price above a sane ceiling in the DEFAULT (cap-less) config.\n * When `maxSpendUsd` is set, `assertSpend` already bounds the op price; when it is NOT set,\n * a compromised or spoofed worker could otherwise answer a routine $0.005 read with a 402\n * challenge for the wallet's whole balance and the client would sign the EIP-3009\n * authorization. Callers who genuinely need a pricier op opt in via `maxSpendUsd`.\n */\n private assertOpPriceCeiling(usd: number): void {\n if (this.maxSpendUsd === undefined && usd > DEFAULT_MAX_OP_USD) {\n throw new SpendCapError(\n `server-quoted op price $${usd} exceeds the built-in $${DEFAULT_MAX_OP_USD} op ceiling; ` +\n \"set maxSpendUsd to allow a higher per-op charge\",\n );\n }\n }\n\n /**\n * The effective per-op ceiling (USD) for an inline-payer op: the caller's `maxSpendUsd`\n * when set (they opted into that bound), else the built-in default ceiling. Handed to the\n * hook as its hard `maxAmountAtomic`, and pre-reserved against the session cap before paying.\n */\n private inlineOpCeilingUsd(): number {\n return this.maxSpendUsd ?? DEFAULT_MAX_OP_USD;\n }\n\n /**\n * The on-chain settlement txHash from a response's PAYMENT-RESPONSE header, or \"\"\n * when the server served the op from existing credits (so the attached top-off\n * authorization was NEVER settled — it just expires) or the header is absent. The\n * worker emits PAYMENT-RESPONSE = base64(JSON `{ success, payer, amount, txHash }`)\n * on any paid 200, with `txHash: \"\"` on the credit hot path. A proactive single-shot\n * top-off must only count toward session spend when this is non-empty — otherwise no\n * USDC moved and recording it inflates sessionSpentUsd, prematurely tripping the cap.\n *\n * Accepted trade-off: in a doubly-rare crash window (the server's settle mined on-chain\n * but its ledger row was lost, AND the response was lost so the client retries), the\n * worker's already-used-authorization recovery returns success with txHash \"\" even\n * though USDC did move. The client then under-counts that top-off by one, making the\n * local session cap marginally lenient — no funds are lost (the amount is still minted\n * as credits). This is unavoidable (the worker cannot distinguish that case from a\n * plain credit-served op) and far cheaper than the systematic L3 over-count it replaces.\n */\n private settledTxHash(res: Response): string {\n const header = res.headers.get(\"PAYMENT-RESPONSE\");\n if (!header) return \"\";\n try {\n // UTF-8 decode to mirror the backend's base64/UTF-8 encoding (see decodeBase64Utf8).\n const parsed = JSON.parse(decodeBase64Utf8(header)) as { txHash?: unknown };\n return typeof parsed.txHash === \"string\" ? parsed.txHash : \"\";\n } catch {\n return \"\";\n }\n }\n\n /**\n * Issue a request with bounded internal retry on TRANSIENT failures only: a\n * thrown fetch (network error / lost response) or a 5xx. `build()` is re-invoked\n * per attempt so the credit path can re-sign identity with a FRESH nonce each\n * time, while the op's stable Idempotency-Key (and pinned EIP-3009 nonce on paid\n * ops) makes a retry of an already-processed request dedupe server-side — so a\n * lost response that the server already charged is recovered without a second\n * charge. NOT retried: any 2xx/4xx (incl. the 402 credit->pay handoff, 401, 404)\n * — those are returned as-is for the caller's normal handling. The bound is kept\n * small so a re-sent paid authorization cannot outlive its validBefore.\n *\n * The retry MECHANICS (transient-status detection, backoff,\n * Retry-After honoring) were extracted to `@agentx402-ai/core`'s `fetchWithRetry`\n * as a pure function parameterized by `maxRetries` — this method is now a\n * thin delegating wrapper so every existing `this.fetchWithRetry(...)` call\n * site above is unchanged.\n */\n private fetchWithRetry(\n url: string,\n build: () => RequestInit | Promise<RequestInit>,\n ): Promise<Response> {\n return fetchWithRetry(url, build, this.maxRetries, {\n timeoutMs: this.timeoutMs,\n fetchImpl: this.fetchImpl,\n });\n }\n\n // --- Discounted Prepay helpers -------------------------------------------\n\n /**\n * Update prepay tracking from any server response. Reads the exact integer\n * credit balance (`X-AgentKV-Credits-Remaining`) and caches the most recent\n * `PAYMENT-REQUIRED` challenge as the proactive single-shot template (so a\n * top-off needs no preflight request). Safe to call when prepay is disabled.\n */\n private trackBalance(res: Response): void {\n const credits = res.headers.get(\"X-AgentKV-Credits-Remaining\");\n if (credits !== null && credits !== \"\") {\n const n = Number(credits);\n if (Number.isFinite(n)) this.knownCredits = n;\n }\n const challenge = res.headers.get(\"PAYMENT-REQUIRED\");\n if (challenge) this.challengeTemplate = challenge;\n }\n\n /** Watermark (USD) expressed in EXACT integer credits (1 credit = CREDIT_VALUE_USD = $0.0001,\n * so $1 = 10,000 credits — matching the worker's mint rate). */\n private watermarkCredits(): number {\n return Math.round(this.prepay!.watermark / CREDIT_VALUE_USD);\n }\n\n /**\n * Synchronous single-flight claim for a proactive top-off. CRITICAL: there is\n * NO `await` between the watermark check and setting `topoffInFlight = true`,\n * and this must be called at the very top of set/get before any await.\n * Otherwise two concurrent ops both pass the check and each fire a separate\n * top-off with distinct fresh nonces the server can't dedupe (double charge).\n * Returns true for exactly one concurrent op below the watermark; the caller\n * MUST clear `topoffInFlight` in a `finally`. Losers take the identity path.\n */\n private tryClaimTopoff(): boolean {\n if (\n !this.prepay ||\n this.prepay.async ||\n this.topoffInFlight ||\n this.knownCredits === undefined ||\n this.knownCredits >= this.watermarkCredits()\n ) {\n return false;\n }\n this.topoffInFlight = true;\n return true;\n }\n\n /**\n * Single-flight claim at a hard 402 (insufficient credits). Unlike\n * `tryClaimTopoff` this ignores the watermark — a 402 already proves credits\n * are short — but still claims the flag synchronously so concurrent 402s don't\n * each fire a top-off. Returns true only if the flag was free; the caller MUST\n * clear it in a `finally`. In `async` mode we leave the 402 to be paid at the\n * op price (the background deposit replenishes credits separately).\n */\n private tryClaimTopoffOnFault(): boolean {\n if (!this.prepay || this.prepay.async || this.topoffInFlight) return false;\n this.topoffInFlight = true;\n return true;\n }\n\n /**\n * Run a synchronous account-mode top-off while PUBLISHING its in-flight promise, so a\n * concurrent op that hits a hard 402 (and can't claim the single-flight) can await THIS\n * deposit and retry rather than surfacing the 402. The caller holds the single-flight claim.\n */\n private async runSharedTopoff(): Promise<void> {\n const p = this.runAccountTopoff();\n this.topoffPromise = p;\n try {\n await p;\n } finally {\n if (this.topoffPromise === p) this.topoffPromise = undefined;\n }\n }\n\n /**\n * Detached async top-off (opt-in via `prepay.async`). When below the watermark\n * and no top-off is in flight, fire a deposit WITHOUT awaiting it in the op\n * path. Documented races: the balance read is point-in-time so the trigger can\n * be stale, and a deposit settling between an op's read and this check can make\n * the top-off redundant; the single-flight flag bounds these to at most one\n * outstanding deposit, but cannot serialize against an op already past its own\n * check. Use the (default) synchronous single-shot for exactly-bounded spend.\n *\n * Account-key mode: there is no signing wallet, so the detached\n * deposit is dispatched through `runAccountTopoff()` (the `topoffPayer` hook)\n * instead of `runDeposit` (which throws `no_signer` in account mode). Wallet\n * mode is completely unchanged below.\n */\n private maybeAsyncTopoff(): void {\n if (\n !this.prepay?.async ||\n this.topoffInFlight ||\n this.knownCredits === undefined ||\n this.knownCredits >= this.watermarkCredits()\n ) {\n return;\n }\n // Budget: a top-off is checked against the SESSION cap only (never the per-op\n // cap). If it would exceed the session cap, skip rather than throw.\n if (\n this.maxSessionSpendUsd !== undefined &&\n this.sessionSpentUsd + this.prepay.topoff > this.maxSessionSpendUsd\n ) {\n return;\n }\n this.topoffInFlight = true;\n if (this.accountKey) {\n // Account-key mode: no signing wallet — dispatch the SAME payer hook the\n // synchronous paths use. A hook failure is swallowed (crash-safe, mirrors\n // the wallet-mode runDeposit catch below); the next op's 402 retries it.\n void this.runAccountTopoff()\n .catch(() => {})\n .finally(() => {\n this.topoffInFlight = false;\n });\n return;\n }\n // Detached + crash-safe: bypass the per-op cap (a top-off is a credit purchase,\n // not a per-op charge) AND swallow any rejection (cap race, network, server)\n // so a failed background top-off never becomes an unhandled rejection that\n // crashes the host — the next op's 402 retries it. deposit() recordSpends itself.\n void this.runDeposit(this.prepay.topoff, { bypassPerOpCap: true })\n .catch(() => {})\n .finally(() => {\n this.topoffInFlight = false;\n });\n }\n\n /**\n * Whether a top-off of `prepay.topoff` fits under the cumulative SESSION cap.\n * Top-offs are deliberately NOT gated on the per-op `maxSpendUsd` (which bounds\n * individual pay-per-op charges); when over the session cap we downgrade to\n * pay-per-op rather than throwing.\n */\n private topoffFitsSessionCap(): boolean {\n if (this.maxSessionSpendUsd === undefined) return true;\n return this.sessionSpentUsd + this.prepay!.topoff <= this.maxSessionSpendUsd;\n }\n\n /**\n * Single source of truth for BOTH the EIP-712-signed pathname (`path`) and the\n * URL to fetch (`url`), so they can never diverge — a divergence silently\n * breaks identity auth: the worker verifies over the RECEIVED path and\n * recovers a phantom address if it differs from what the client signed.\n * `base` is the un-prefixed pathname; the fetched/signed path is `/v1` + `base`\n * (list-keys overrides it to `/v1/kv`). `query` is appended to `url`\n * ONLY — EIP-712 binds the pathname, never the query string.\n */\n private route(spec: { base: string; versioned?: string; query?: string }): {\n path: string;\n url: string;\n } {\n const path = spec.versioned ?? `${V1}${spec.base}`;\n const q = spec.query ? `?${spec.query}` : \"\";\n return { path, url: `${this.endpoint}${path}${q}` };\n }\n\n // kv entry route (set/get/delete). `digest` is base64url (from hashKey(), which\n // returns toBase64Url output — URL-safe [A-Za-z0-9_-], not hex), so no extra\n // encoding is needed and the signed path matches the fetched path byte-for-byte.\n private kvRoute(digest: string): { path: string; url: string } {\n return this.route({ base: `/kv/${digest}` });\n }\n\n /**\n * Per-op auth headers. In account-key mode this is the `Authorization: Bearer\n * ak_…` header (server hashes it to name storage + debit credits); in wallet\n * mode it is the EIP-712 identity signature. Used by every op so the same call\n * site picks the right scheme. Async to share the signature of `identityHeaders`.\n */\n private async authHeaders(method: string, path: string): Promise<Record<string, string>> {\n if (this.accountKey) return buildBearerHeaders(this.accountKey);\n return { ...(await this.identityHeaders(method, path)) };\n }\n\n /**\n * The signing wallet, asserted present. Every x402 path (set/get top-off + 402\n * fallback, deposit) is gated behind `!this.accountKey` and so always has a\n * signer; this narrows the optional type at those call sites (and fails loudly\n * if that invariant is ever broken).\n */\n private requireSigner(): Signer {\n if (!this.signer) {\n throw new AgentKVError(\n \"no signer: this operation requires a signing wallet\",\n \"invalid_config\",\n 0,\n );\n }\n return this.signer;\n }\n\n /** EIP-712 identity headers with the deployment host bound into the signature (prevents cross-deployment signature replay). */\n private identityHeaders(method: string, path: string) {\n if (!this.signer) {\n // Unreachable in account-key mode (those ops use the bearer path); a guard\n // so the wallet-only signing surface never silently no-ops.\n throw new AgentKVError(\n \"no signer: this operation requires a signing wallet\",\n \"invalid_config\",\n 0,\n );\n }\n return buildIdentityHeaders(this.signer, {\n method,\n path,\n host: new URL(this.endpoint).host,\n network: this.network,\n });\n }\n\n /**\n * Shared money/transport orchestrator behind set() and getInternal() — the single copy of\n * the flow both share: account-key (bearer) mode with proactive + hard-402 top-off and the\n * inline-payer path; wallet mode with the proactive single-shot, credit path, and 402\n * pay-and-retry — including the single-flight top-off accounting and settled-txHash spend\n * gating. Per-op differences (method/body, credit cost, 404 handling, success/inline\n * parsing) come from `spec`. The CALLER must claim the single-flight top-off SYNCHRONOUSLY\n * (before any await) and pass it in `flight`; it may be re-claimed here on a cold-start hard\n * 402, and the caller's `finally` releases it.\n */\n private async performOp<T>(\n flight: { claimed: boolean },\n spec: {\n method: \"POST\" | \"GET\";\n path: string;\n url: string;\n idempotencyKey: string;\n creditCostUsd: number;\n label: string;\n buildRequest: (headers: Record<string, string>) => RequestInit;\n parseSuccess: (res: Response) => Promise<T>;\n parseInline: (inlineRes: OpInlineResponse) => Promise<T>;\n /** Return value for a 404 (get: `{ value: null }`); omitted for set (404 -> error). */\n notFound?: () => T;\n },\n ): Promise<T> {\n const { path, url, idempotencyKey, creditCostUsd, label } = spec;\n\n // Account-key mode: bearer auth debits prepaid credits server-side. No x402/EIP-712 — a\n // 402 (insufficient credits) carries no challenge. Cap the spend at the credit cost.\n if (this.accountKey) {\n // Request-scoped: true once a top-off DEPOSIT actually succeeded for THIS op — bounds\n // spend to at most one real on-chain deposit per op (see the hard-402 guard below).\n let toppedOff = false;\n // Proactive watermark top-off (single-flight claim held): delegate to the payer hook. A\n // failure here is NON-fatal (credits may still cover the op; the hard-402 path below\n // surfaces a real shortfall). Not setting toppedOff on a failed proactive deposit is\n // deliberate: it deposited nothing, so the hard-402 path may still try exactly one.\n if (flight.claimed && this.topoffPayer && this.topoffFitsSessionCap()) {\n try {\n await this.runSharedTopoff();\n toppedOff = true;\n } catch {\n // swallowed by design (proactive path); the op continues on remaining credits.\n }\n }\n this.maybeAsyncTopoff();\n this.assertSpend(creditCostUsd);\n const sendBearer = () =>\n this.fetchWithRetry(url, () =>\n spec.buildRequest({\n \"Idempotency-Key\": idempotencyKey,\n ...buildBearerHeaders(this.accountKey!),\n }),\n );\n let res = await sendBearer();\n this.trackBalance(res);\n // Hard 402: with a payer hook, buy a top-off and retry ONCE (same key = exactly-once).\n // Skipped after a successful proactive deposit (`!toppedOff`) so at most one deposit/op.\n if (res.status === 402 && this.topoffPayer && !toppedOff) {\n if (!flight.claimed) flight.claimed = this.tryClaimTopoffOnFault();\n if (flight.claimed && this.topoffFitsSessionCap()) {\n await this.runSharedTopoff();\n res = await sendBearer();\n this.trackBalance(res);\n } else if (this.topoffPromise) {\n // A concurrent op won the single-flight and is depositing RIGHT NOW: rather than\n // surface this 402 (a deposit is landing), await that sibling's top-off and retry\n // the bearer ONCE — the same Idempotency-Key keeps it exactly-once.\n await this.topoffPromise.catch(() => {});\n res = await sendBearer();\n this.trackBalance(res);\n }\n }\n // Inline opt-in: route the WHOLE op through an external x402 transport (e.g. awal)\n // instead of a credit top-off. Mutually exclusive with topoffPayer PER OP.\n if (res.status === 402 && this.opInlinePayer && !this.topoffPayer) {\n // Bound by the caller's per-op cap and pre-reserve against the session cap BEFORE\n // paying — the credit-cost pre-flight only checked the credit price, not real USDC.\n const inlineCeilingUsd = this.inlineOpCeilingUsd();\n this.assertSpend(inlineCeilingUsd);\n const reqInit = spec.buildRequest({\n \"Idempotency-Key\": idempotencyKey,\n ...buildBearerHeaders(this.accountKey!),\n });\n const inlineRes = await this.opInlinePayer({\n url,\n method: spec.method,\n body: reqInit.body as string | undefined,\n headers: reqInit.headers as Record<string, string>,\n // The hook MUST NOT settle more than the effective per-op ceiling.\n maxAmountAtomic: Math.round(inlineCeilingUsd * 1_000_000),\n });\n if (inlineRes.status === 404 && spec.notFound) return spec.notFound();\n if (inlineRes.status < 200 || inlineRes.status >= 300) {\n throw this.errorFromBody(inlineRes.status, inlineRes.body, label);\n }\n this.recordSpend(this.inlineSettledAmountUsd(inlineRes.headers) ?? creditCostUsd);\n return spec.parseInline(inlineRes);\n }\n if (res.status === 404 && spec.notFound) return spec.notFound();\n if (!res.ok) throw await this.asError(res, label);\n this.recordSpend(creditCostUsd);\n return spec.parseSuccess(res);\n }\n\n // 0) Wallet-mode proactive single-shot top-off (claim held): pay a >=$1 top-off on THIS op\n // from the cached challenge template. Cold start (no template) -> identity path below.\n if (flight.claimed && this.challengeTemplate && this.topoffFitsSessionCap()) {\n let paymentSignature: string | undefined;\n try {\n paymentSignature = await buildPaymentHeader(this.requireSigner(), this.challengeTemplate, {\n amountAtomic: this.topoffAtomic,\n expectedNetwork: this.network,\n // Pin the nonce to the op's idempotency key so a retry reuses the auth and the\n // server dedupes the mint + the op.\n nonce: nonceFromIdempotencyKey(idempotencyKey),\n });\n } catch {\n // Corrupted/stale cached template or a network-pin failure: clear it and fall through\n // to the identity path (the hard-402 fallback refreshes the template).\n this.challengeTemplate = undefined;\n }\n if (paymentSignature !== undefined) {\n const res = await this.fetchWithRetry(url, () =>\n spec.buildRequest({\n \"Idempotency-Key\": idempotencyKey,\n \"PAYMENT-SIGNATURE\": paymentSignature as string,\n }),\n );\n this.trackBalance(res);\n if (res.status === 404 && spec.notFound) return spec.notFound();\n // A 402 means the cached template was stale (trackBalance just refreshed it): fall\n // through to the identity/credit path, self-healing on THIS call (same held claim).\n if (res.status !== 402) {\n if (!res.ok) throw await this.asError(res, label);\n // Count the top-off ONLY if it actually settled on-chain (non-empty PAYMENT-RESPONSE\n // txHash) — a credit-served op settles nothing; single-flight => at most once.\n if (this.settledTxHash(res)) this.recordSpend(this.prepay!.topoff);\n return spec.parseSuccess(res);\n }\n }\n }\n\n // Async mode: kick off a detached background deposit (opt-in, not awaited).\n this.maybeAsyncTopoff();\n\n // 1) Credit path: an EIP-712 identity signature spends pre-paid credits with no on-chain\n // settlement. Re-sign identity with a FRESH nonce per transient retry; the stable\n // Idempotency-Key carries dedup.\n let res = await this.fetchWithRetry(url, async () =>\n spec.buildRequest({\n \"Idempotency-Key\": idempotencyKey,\n ...(await this.identityHeaders(spec.method, path)),\n }),\n );\n this.trackBalance(res);\n\n // 2) Insufficient credits -> 402 x402 challenge: pay and retry with the same key.\n if (res.status === 402) {\n const challenge = res.headers.get(\"PAYMENT-REQUIRED\");\n if (!challenge) {\n throw await this.asError(res, \"payment required but no PAYMENT-REQUIRED challenge\");\n }\n // Prepay hard-402 fallback: pay a TOP-OFF (>=$1) instead of the op price. Claim the\n // single-flight now if we didn't already (cold start). Over the session cap -> pay-per-op.\n if (!flight.claimed) flight.claimed = this.tryClaimTopoffOnFault();\n const topoffHere = flight.claimed && this.topoffFitsSessionCap();\n const usd = topoffHere\n ? this.prepay!.topoff\n : challengePriceUsd(challenge, undefined, this.network);\n if (!topoffHere) {\n this.assertOpPriceCeiling(usd);\n this.assertSpend(usd);\n }\n // Pin the EIP-3009 nonce to the idempotency key so a retried op reuses the same\n // authorization and the server dedupes. Re-send the identical signed header on retry.\n const paymentSignature = await buildPaymentHeader(this.requireSigner(), challenge, {\n amountAtomic: topoffHere ? this.topoffAtomic : undefined,\n expectedNetwork: this.network,\n nonce: nonceFromIdempotencyKey(idempotencyKey),\n });\n res = await this.fetchWithRetry(url, () =>\n spec.buildRequest({\n \"Idempotency-Key\": idempotencyKey,\n \"PAYMENT-SIGNATURE\": paymentSignature,\n }),\n );\n this.trackBalance(res);\n // Settlement gate (L3): count a TOP-OFF only when it settled (non-empty txHash); a\n // concurrent sibling can mint credits between the 402 and the retry (txHash \"\"). The\n // op-price branch (!topoffHere) stays on res.ok — it is the real op cost.\n if (res.ok && (!topoffHere || this.settledTxHash(res))) this.recordSpend(usd);\n }\n\n if (res.status === 404 && spec.notFound) return spec.notFound();\n if (!res.ok) throw await this.asError(res, label);\n return spec.parseSuccess(res);\n }\n\n /**\n * Write an encrypted value. The value is JSON-stringified and AES-256-GCM\n * encrypted client-side; the server only ever stores ciphertext. `null` and\n * `undefined` are rejected (`invalid_value`) so a `null` from get() unambiguously\n * means \"missing key\" — use delete() to remove a key. Tries the credit path first\n * (EIP-712 identity signature); if credits are insufficient the server returns a 402\n * x402 challenge and the client pays. A stable Idempotency-Key is reused across that\n * retry so the write is exactly-once.\n */\n async set(key: string, value: unknown, opts: SetOptions = {}): Promise<SetResult> {\n // CRITICAL single-flight: claim a proactive top-off SYNCHRONOUSLY, before any\n // await (encryption etc.). Exactly one concurrent op below the watermark wins.\n // The claim may also be taken later at a hard 402 (cold-start fallback) inside performOp().\n const flight = { claimed: this.tryClaimTopoff() };\n try {\n const plaintext = JSON.stringify(value);\n // Reject null/undefined (and anything that stringifies to undefined: functions,\n // symbols). Stored values are always a defined JSON value, so a null from get()\n // unambiguously means \"missing key\" — never \"a stored null\". Use delete() to remove.\n if (value === null || plaintext === undefined) {\n throw new AgentKVError(\n \"cannot store null or undefined; use delete() to remove a key\",\n \"invalid_value\",\n 0,\n );\n }\n const km = await this.getKeyMaterial();\n // Hide the key NAME too: address the server by an opaque per-wallet digest and\n // ship the encrypted name alongside (for list-keys) — never the plaintext name.\n const digest = hashKey(km.mac, key);\n // Bind the digest into the value's AAD so the server can't later serve this ciphertext\n // for a DIFFERENT key's request without failing the auth tag (substitution defense).\n const ciphertext = await encrypt(km.value, plaintext, digest);\n const body: Record<string, unknown> = {\n value: ciphertext,\n key_name: await encrypt(km.keyName, key),\n };\n // camelCase API option -> snake_case wire field.\n if (opts.ttlDays !== undefined) body.ttl_days = opts.ttlDays;\n if (opts.strictTtl !== undefined) body.strict_ttl = opts.strictTtl;\n const payload = JSON.stringify(body);\n\n const idempotencyKey = opts.idempotencyKey ?? freshNonce();\n const { path, url } = this.kvRoute(digest);\n\n return await this.performOp<SetResult>(flight, {\n method: \"POST\",\n path,\n url,\n idempotencyKey,\n creditCostUsd: ACCOUNT_WRITE_USD,\n label: \"set failed\",\n buildRequest: (headers) => ({\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", ...headers },\n body: payload,\n }),\n parseSuccess: async (res) => (await res.json()) as SetResult,\n parseInline: async (inlineRes) => JSON.parse(inlineRes.body) as SetResult,\n });\n } finally {\n if (flight.claimed) this.topoffInFlight = false;\n }\n }\n\n /**\n * Read and decrypt a value. Tries the credit path first (EIP-712 identity);\n * if credits are insufficient the server returns a 402 x402 challenge and the\n * client pays, then retries. Returns null if the key is missing or expired (404);\n * stored values are never null (set rejects it), so null unambiguously means absent.\n */\n async get<T = unknown>(key: string, opts: GetOptions = {}): Promise<T | null> {\n const { value } = await this.getInternal<T>(key, opts);\n return value;\n }\n\n /**\n * Like `get`, but ALSO surfaces the machine-readable usage envelope the server\n * attaches to a paid read's success body — a separate, additive\n * accessor so `get()` keeps its narrower `T | null` signature (no existing\n * caller breaks). `usage` is absent when the key was missing/expired (404 —\n * the read op itself is never charged on a miss, see the DO's 404-before-charge\n * precheck — though a proactive top-off, if one was triggered for this call,\n * is a separate deposit charge and can still happen alongside a miss) or when\n * talking to a server that predates the usage envelope.\n */\n async getWithUsage<T = unknown>(\n key: string,\n opts: GetOptions = {},\n ): Promise<{ value: T | null; usage?: UsageBlock }> {\n return this.getInternal<T>(key, opts);\n }\n\n /**\n * Shared implementation behind `get`/`getWithUsage`. Tries the credit path\n * first (EIP-712 identity); if credits are insufficient the server returns a\n * 402 x402 challenge and the client pays, then retries. `value` is null if\n * the key is missing or expired (404); stored values are never null (set\n * rejects it), so null unambiguously means absent.\n */\n private async getInternal<T = unknown>(\n key: string,\n opts: GetOptions = {},\n ): Promise<{ value: T | null; usage?: UsageBlock }> {\n // CRITICAL single-flight: claim a proactive top-off SYNCHRONOUSLY, before any\n // await. Exactly one concurrent op below the watermark wins; losers read.\n // The claim may also be taken later at a hard 402 (cold-start fallback) inside performOp().\n const flight = { claimed: this.tryClaimTopoff() };\n try {\n const digest = hashKey((await this.getKeyMaterial()).mac, key);\n const { path, url } = this.kvRoute(digest);\n // Stable per-op key (fresh per call unless the caller supplies one): sent as\n // Idempotency-Key on the credit path and pinned into the EIP-3009 nonce on the\n // paid path, so an internal retry of a lost-response read dedupes server-side\n // (the read idempotency record returns the cached value) instead of charging\n // twice. Two SEPARATE get()s still use distinct keys (separately charged).\n const idempotencyKey = opts.idempotencyKey ?? freshNonce();\n const parseBody = async (raw: string): Promise<{ value: T | null; usage?: UsageBlock }> => {\n const data = JSON.parse(raw) as { value: string; usage?: UsageBlock };\n const decryptedText = await this.decryptValue(data.value, key);\n return { value: JSON.parse(decryptedText) as T, usage: data.usage };\n };\n\n return await this.performOp<{ value: T | null; usage?: UsageBlock }>(flight, {\n method: \"GET\",\n path,\n url,\n idempotencyKey,\n creditCostUsd: ACCOUNT_READ_USD,\n label: \"get failed\",\n buildRequest: (headers) => ({ method: \"GET\", headers }),\n parseSuccess: async (res) => parseBody(await res.text()),\n parseInline: async (inlineRes) => parseBody(inlineRes.body),\n notFound: () => ({ value: null }),\n });\n } finally {\n if (flight.claimed) this.topoffInFlight = false;\n }\n }\n\n /**\n * Delete a key. Free operation. Authenticated with the account-key bearer in\n * account mode, else an EIP-712 identity signature (fresh nonce + timestamp).\n * The digest is computed from the local key material either way.\n */\n async delete(key: string): Promise<DeleteResult> {\n const digest = hashKey((await this.getKeyMaterial()).mac, key);\n const { path, url } = this.kvRoute(digest);\n // Route through fetchWithRetry (consistent with set/get/deposit): re-sign identity\n // with a FRESH nonce per attempt so a transient 5xx/network retry is not a replay.\n const res = await this.fetchWithRetry(url, async () => ({\n method: \"DELETE\",\n headers: { ...(await this.authHeaders(\"DELETE\", path)) },\n }));\n if (!res.ok) {\n throw await this.asError(res, \"delete failed\");\n }\n return (await res.json()) as DeleteResult;\n }\n\n /**\n * List the wallet's keys. The server returns opaque per-wallet digests plus each key's\n * ENCRYPTED name; this decrypts the names locally and returns them — the server never\n * sees a plaintext key name. Free (EIP-712 identity signed). Paginated: pass the returned\n * `cursor` to fetch the next page; `cursor` is null once exhausted.\n */\n async listKeys(\n opts: { cursor?: string | null; limit?: number } = {},\n ): Promise<{ keys: string[]; cursor: string | null }> {\n const km = await this.getKeyMaterial();\n // EIP-712 binds the pathname only (query excluded); the v1 canonical list path is\n // `/v1/kv` (NOT `/v1/list-keys`), so the versioned pathname is given explicitly.\n const params = new URLSearchParams();\n if (opts.cursor) params.set(\"cursor\", opts.cursor);\n if (opts.limit !== undefined) params.set(\"limit\", String(opts.limit));\n const qs = params.toString();\n const { path, url } = this.route({\n base: \"/list-keys\",\n versioned: `${V1}/kv`,\n query: qs || undefined,\n });\n // Route through fetchWithRetry (consistent with set/get/deposit): re-sign identity\n // with a FRESH nonce per attempt so a transient retry is not a nonce replay.\n const res = await this.fetchWithRetry(url, async () => ({\n method: \"GET\",\n headers: { ...(await this.authHeaders(\"GET\", path)) },\n }));\n if (!res.ok) throw await this.asError(res, \"list-keys failed\");\n const data = (await res.json()) as {\n items: { key: string; key_name: string | null }[];\n cursor: string | null;\n };\n // Decrypt each encrypted name locally (legacy entries without one are skipped). Tolerate\n // a single undecryptable name (an entry written under a rotated key, or a corrupted\n // key_name blob): skip it rather than reject the whole listing, so one bad row can't make\n // every healthy key unlistable (and undiscoverable for cleanup).\n const keys = (\n await Promise.all(\n data.items\n .filter((i): i is { key: string; key_name: string } => i.key_name != null)\n .map(async (i) => {\n try {\n return await decrypt(km.keyName, i.key_name);\n } catch {\n return null;\n }\n }),\n )\n ).filter((k): k is string => k !== null);\n return { keys, cursor: data.cursor };\n }\n\n /**\n * Read the pre-paid credit balance. Free. Account-key bearer in account mode,\n * else an EIP-712 identity signature.\n */\n async balance(): Promise<number> {\n const { path, url } = this.route({ base: \"/credits/balance\" });\n // Route through fetchWithRetry (consistent with set/get/deposit): re-sign identity\n // with a FRESH nonce per attempt so a transient retry is not a nonce replay.\n const res = await this.fetchWithRetry(url, async () => ({\n method: \"GET\",\n headers: { ...(await this.authHeaders(\"GET\", path)) },\n }));\n this.trackBalance(res);\n if (!res.ok) {\n throw await this.asError(res, \"balance failed\");\n }\n const body = (await res.json()) as { balance: number };\n // Authoritative balance from the body keeps prepay tracking exact even when\n // the header is absent (e.g. a CORS-stripped header on some transports).\n if (this.prepay && Number.isFinite(body.balance)) this.knownCredits = body.balance;\n return body.balance;\n }\n\n /**\n * Buy credits with an x402 payment. `amountUsd` must be at least $1; any\n * amount is accepted (no fixed tiers). This settles on-chain once via the\n * facilitator; the returned credits are then spendable by set/get with no\n * further payment.\n */\n async deposit(\n amountUsd: number,\n opts: { idempotencyKey?: string; expectedPayTo?: string } = {},\n ): Promise<DepositResult> {\n // Public API always honors the per-op cap. The internal top-off path\n // (maybeAsyncTopoff) calls runDeposit() directly to bypass it — the bypass is\n // not part of the public surface, so a caller can't disable their own cap.\n //\n // Account-key mode: there is no signing wallet to run runDeposit()'s\n // x402 flow. With a configured topoffPayer, alias to it instead — symmetric\n // with wallet-mode deposit(): ask the hook to buy `amountUsd` of credits, then\n // report the resulting balance. This works even though deposit() may be\n // called with no `prepay` configured (runAccountTopoff's explicit-amount path\n // does not touch `prepay`). Without a topoffPayer, fall through unchanged to\n // runDeposit()'s existing no_signer error below — account-key mode has no\n // other in-SDK way to pay.\n if (this.accountKey && this.topoffPayer) {\n const runAccountDeposit = async (): Promise<DepositResult> => {\n this.assertSpend(amountUsd);\n await this.runAccountTopoff(amountUsd);\n const balance = await this.balance();\n return { credits_added: Math.round(amountUsd / CREDIT_VALUE_USD), balance };\n };\n if (this.topoffInFlight) {\n return runAccountDeposit();\n }\n this.topoffInFlight = true;\n try {\n return await runAccountDeposit();\n } finally {\n this.topoffInFlight = false;\n }\n }\n\n // Claim the top-off single-flight for the deposit's duration so a concurrent op's\n // watermark top-off can't fire a SECOND on-chain purchase while this deposit is\n // settling (knownCredits stays stale-low until runDeposit refreshes it). If a top-off\n // is already in flight, or prepay is off, just run — no extra guard needed.\n if (!this.prepay || this.topoffInFlight) {\n return this.runDeposit(amountUsd, opts);\n }\n this.topoffInFlight = true;\n try {\n return await this.runDeposit(amountUsd, opts);\n } finally {\n this.topoffInFlight = false;\n }\n }\n\n /**\n * Account-key top-off: delegate payment of `${endpoint}/account/deposit` to the\n * configured `topoffPayer` (a managed account has no signing wallet to sign an\n * x402 payment). The caller must hold the single-flight claim and have checked\n * `topoffFitsSessionCap()`. On resolve (= the deposit SETTLED) the top-off is\n * recorded against the session budget only — top-offs are credit purchases, not\n * per-op charges, so the per-op cap is deliberately not consulted (mirrors the\n * wallet-mode top-off budget rules). A rejection is wrapped as\n * `account_topoff_failed`; the ak_ bearer is never included in the message.\n */\n /**\n * `amountUsd` generalizes this beyond the fixed `prepay.topoff` amount\n * so `deposit()` can reuse it for a caller-chosen amount. OMITTED (the no-arg\n * call from the proactive/hard-402/async paths above), it defaults to\n * `prepay.topoff` and its precomputed `topoffAtomic` ceiling — BYTE-FOR-BYTE the\n * stage-1 behavior. An EXPLICIT `amountUsd` (from `deposit()`, which may be\n * called with no `prepay` configured at all) is validated here the same way\n * `runDeposit` validates its wallet-mode amount, since it never passed through\n * the constructor's `prepay.topoff` guard.\n */\n private async runAccountTopoff(amountUsd?: number): Promise<void> {\n let amount: number;\n let maxAmountAtomic: number;\n if (amountUsd === undefined) {\n amount = this.prepay!.topoff;\n maxAmountAtomic = this.topoffAtomic;\n } else {\n const atomic = toWholeAtomicUsd(amountUsd);\n if (atomic === null || !(amountUsd >= 1)) {\n throw new AgentKVError(\n \"amountUsd must be >= $1 and a whole number of atomic USDC units\",\n \"invalid_config\",\n 0,\n );\n }\n amount = amountUsd;\n maxAmountAtomic = atomic;\n }\n try {\n await this.topoffPayer!({\n depositUrl: this.route({ base: \"/account/deposit\" }).url,\n accountKey: this.accountKey!,\n amountUsd: amount,\n maxAmountAtomic,\n });\n } catch (e) {\n const detail = e instanceof Error ? e.message : String(e);\n throw new AgentKVError(\n `account top-off failed: ${detail} — check the payer wallet's USDC balance (e.g. 'awal balance'; fund by sending USDC to its address)`,\n \"account_topoff_failed\",\n 0,\n );\n }\n this.recordSpend(amount);\n }\n\n private async runDeposit(\n amountUsd: number,\n opts: { bypassPerOpCap?: boolean; idempotencyKey?: string; expectedPayTo?: string },\n ): Promise<DepositResult> {\n // Account-key mode has NO signing wallet, so it cannot sign an x402 payment. This is\n // only reached when NO topoffPayer is configured — deposit() aliases to the payer\n // hook instead of runDeposit when one is set (see deposit() above). Fund it\n // instead: fundAccount(payer, amountUsd) is the in-SDK path (an external payer wallet\n // credits this account's namespace); the CLI/awal routes remain for out-of-process funding.\n if (this.accountKey) {\n throw new AgentKVError(\n \"Account-key mode has no signing wallet. Fund this account with \" +\n \"fundAccount(payerKeyOrSigner, amountUsd), or via 'agentkv fund', or awal: \" +\n `awal x402 pay ${this.route({ base: \"/account/deposit\" }).url} --headers '{\"Authorization\":\"Bearer <ak>\"}'.`,\n \"no_signer\",\n 0,\n );\n }\n // Validate to a whole number of atomic USDC units before any network call —\n // the server's only check is the >= $1 floor, so a fractional amount (e.g.\n // 1.0000001) would otherwise reach the facilitator and 400 with a cryptic\n // error. Mirrors the prepay.topoff guard in the constructor.\n const amountAtomic = toWholeAtomicUsd(amountUsd);\n if (amountAtomic === null || !(amountUsd >= 1)) {\n throw new AgentKVError(\n \"deposit amountUsd must be >= $1 and a whole number of atomic USDC units\",\n \"invalid_config\",\n 0,\n );\n }\n this.assertSpend(amountUsd, opts);\n // Stable per-deposit key: pin the EIP-3009 nonce to it so a transient retry of a\n // settled-but-unacked deposit reuses the authorization and the server dedupes\n // (replaying the prior result, or rejecting the already-used authorization)\n // instead of settling + minting twice.\n // Caller-supplied key makes a caller-level retry of a settled-but-unacked deposit safe\n // (the pinned nonce dedupes server-side); else a fresh key per call.\n const opKey = opts.idempotencyKey ?? freshNonce();\n const { url } = this.route({ base: \"/credits/deposit\" });\n // First request triggers a 402 challenge; then we sign the payment.\n let res = await this.fetchWithRetry(url, () => ({\n method: \"POST\",\n headers: { \"Idempotency-Key\": opKey },\n }));\n this.trackBalance(res);\n if (res.status === 402) {\n const challenge = res.headers.get(\"PAYMENT-REQUIRED\");\n if (!challenge) {\n throw await this.asError(res, \"payment required but no PAYMENT-REQUIRED challenge\");\n }\n const paymentSignature = await buildPaymentHeader(this.requireSigner(), challenge, {\n amountAtomic,\n expectedNetwork: this.network,\n expectedPayTo: opts.expectedPayTo,\n nonce: nonceFromIdempotencyKey(opKey),\n });\n res = await this.fetchWithRetry(url, () => ({\n method: \"POST\",\n headers: { \"Idempotency-Key\": opKey, \"PAYMENT-SIGNATURE\": paymentSignature },\n }));\n this.trackBalance(res);\n }\n if (!res.ok) {\n throw await this.asError(res, \"deposit failed\");\n }\n this.recordSpend(amountUsd);\n const result = (await res.json()) as DepositResult;\n // Refresh prepay tracking with the authoritative post-deposit balance.\n if (this.prepay && Number.isFinite(result.balance)) this.knownCredits = result.balance;\n return result;\n }\n\n /**\n * Fund an ACCOUNT-KEY namespace — \"payer funds, bearer owns\". A CALLER-supplied\n * `signer` pays via x402 to add prepaid credits to THIS client's account (the\n * one named by its `ak_…` bearer). The payer and the owner are deliberately\n * DECOUPLED: the payer wallet signs the on-chain EIP-3009 authorization, while\n * the bearer — not the payer's address — owns the credited namespace. This is the\n * SDK counterpart of the server's `/account/deposit` route.\n *\n * Account-key mode ONLY. In WALLET mode the paying wallet IS the namespace, so\n * use `deposit()` instead — calling this throws `wrong_mode` before any network.\n *\n * `signer` is the PAYER: a viem account (must expose `address` + `signTypedData`)\n * or a raw `0x` private key (built into a viem account internally, mirroring the\n * constructor). `amountUsd` must be a whole number of dollars >= $1. UNLIKE\n * `deposit()` (which IS gated by both spend caps and counts toward session spend),\n * this explicit funding call is NOT gated by `maxSpendUsd`/`maxSessionSpendUsd` and\n * does not count toward session spend — the payer is an EXTERNAL wallet, not this\n * client's tracked budget. The local encryption key is never touched (funding does not encrypt).\n */\n async fundAccount(\n signer: Signer | `0x${string}`,\n amountUsd: number,\n opts: { idempotencyKey?: string; expectedPayTo?: string } = {},\n ): Promise<DepositResult> {\n // Account-key mode ONLY. `fundAccount` funds a DECOUPLED account bearer; a\n // wallet-mode client's paying wallet already IS its namespace (use deposit()).\n if (!this.accountKey) {\n throw new AgentKVError(\n \"fundAccount funds an account-key namespace; in wallet mode use deposit()\",\n \"wrong_mode\",\n 0,\n );\n }\n // Resolve the PAYER, deliberately separate from `this.accountKey` (the owner):\n // a raw 0x private key is built into a viem account; any other value is used\n // as-is as a viem account. Mirrors the constructor's signer handling.\n const payer: Signer = typeof signer === \"string\" ? privateKeyToAccount(signer) : signer;\n // Fail clearly on a bad payer (e.g. undefined, or an object missing address/\n // signTypedData) instead of a cryptic TypeError deep inside buildPaymentHeader.\n if (!payer?.address || typeof payer.signTypedData !== \"function\") {\n throw new AgentKVError(\n \"fundAccount: signer must be a 0x private key or a viem account (with address + signTypedData)\",\n \"invalid_config\",\n 0,\n );\n }\n\n // Validate to a whole number of US dollars >= $1 BEFORE any network call — the\n // server's only check is the >= $1 floor, so a bad amount would otherwise reach\n // the facilitator and 400. (Stricter than deposit()'s whole-atomic guard: an\n // account is funded in whole dollars.) A whole dollar is always whole-atomic.\n if (!Number.isInteger(amountUsd) || amountUsd < 1) {\n throw new AgentKVError(\n \"fundAccount amountUsd must be a whole number of US dollars >= $1\",\n \"invalid_config\",\n 0,\n );\n }\n const amountAtomic = amountUsd * 1_000_000;\n\n const { url } = this.route({ base: \"/account/deposit\" });\n const bearer = buildBearerHeaders(this.accountKey);\n // Stable per-deposit key reused across the challenge->pay retry; pin the EIP-3009\n // nonce to it so a transient retry of a settled-but-unacked deposit reuses the\n // authorization and the server dedupes (exactly-once) instead of settling twice.\n const idempotencyKey = opts.idempotencyKey ?? freshNonce();\n const nonce = nonceFromIdempotencyKey(idempotencyKey);\n\n // 1) Bearer POST with NO payment -> 402 + a PAYMENT-REQUIRED challenge.\n let res = await this.fetchWithRetry(url, () => ({\n method: \"POST\",\n headers: { ...bearer, \"Idempotency-Key\": idempotencyKey },\n }));\n this.trackBalance(res);\n if (res.status === 402) {\n const challenge = res.headers.get(\"PAYMENT-REQUIRED\");\n if (!challenge) {\n throw await this.asError(res, \"payment required but no PAYMENT-REQUIRED challenge\");\n }\n // Sign the x402 payment with the PAYER's wallet (never the account bearer).\n const paymentSignature = await buildPaymentHeader(payer, challenge, {\n amountAtomic,\n expectedNetwork: this.network,\n expectedPayTo: opts.expectedPayTo,\n nonce,\n });\n res = await this.fetchWithRetry(url, () => ({\n method: \"POST\",\n headers: {\n ...bearer,\n \"Idempotency-Key\": idempotencyKey,\n \"PAYMENT-SIGNATURE\": paymentSignature,\n },\n }));\n this.trackBalance(res);\n }\n if (!res.ok) {\n throw await this.asError(res, \"fundAccount failed\");\n }\n const result = (await res.json()) as DepositResult;\n // Refresh prepay tracking (if enabled) with the authoritative post-deposit balance\n // of THIS account — funding credits the same namespace this client reads/writes.\n if (this.prepay && Number.isFinite(result.balance)) this.knownCredits = result.balance;\n return result;\n }\n\n /** Shared by `asError` (a real `Response`) and the `opInlinePayer` path (a plain `{status,body}`). */\n private errorFromBody(status: number, bodyText: string, fallback: string): Error {\n let detail = fallback,\n code = \"request_failed\";\n try {\n const body = JSON.parse(bodyText) as { error?: string; code?: string };\n if (body?.error) detail = body.error;\n if (body?.code) code = body.code;\n } catch {\n /* non-JSON */\n }\n return new AgentKVError(`AgentKV ${status}: ${detail}`, code, status);\n }\n\n private async asError(res: Response, fallback: string): Promise<Error> {\n return this.errorFromBody(res.status, await res.text(), fallback);\n }\n\n /**\n * The settled amount (USD) from an `opInlinePayer` response's PAYMENT-RESPONSE\n * header — the inline-path mirror of `settledTxHash()` above, but reading a\n * plain `Record<string,string>` (the hook's own headers, not a `Response`) and\n * returning the `amount` field instead of the `txHash`. Case-insensitive header\n * lookup: an external transport (e.g. awal) is not guaranteed to preserve the\n * worker's exact `PAYMENT-RESPONSE` casing. Returns `undefined` when the header\n * is absent/unparsable OR the op settled nothing (served from existing credits,\n * `txHash: \"\"`) — callers fall back to the credit-equivalent op price.\n */\n private inlineSettledAmountUsd(headers: Record<string, string>): number | undefined {\n const key = Object.keys(headers).find((k) => k.toLowerCase() === \"payment-response\");\n const header = key ? headers[key] : undefined;\n if (!header) return undefined;\n try {\n const parsed = JSON.parse(decodeBase64Utf8(header)) as {\n amount?: unknown;\n txHash?: unknown;\n };\n if (typeof parsed.txHash !== \"string\" || parsed.txHash === \"\") return undefined;\n const atomic = Number(parsed.amount);\n return Number.isFinite(atomic) && atomic > 0 ? atomic / 1_000_000 : undefined;\n } catch {\n return undefined;\n }\n }\n}\n","// client/src/account.ts\n//\n// Account-key primitives for the \"account-key\" auth model: a stable account\n// decoupled from any paying wallet, authenticated by an opaque `ak_…` bearer\n// token. A managed wallet (e.g. one that cannot sign) presents\n// `Authorization: Bearer ak_…`; the worker hashes it (SHA-256) to name the\n// account's storage and debits prepaid credits — NO x402, NO EIP-712.\n//\n// Mirrors the backend's account-key module (generation + format check). The\n// client only ever holds the RAW key — it never hashes it (the worker does that\n// at rest). Randomness comes from WebCrypto `crypto.getRandomValues`, available\n// in Node >= 20, browsers, and Cloudflare Workers (no node:crypto).\n\nimport { bytesToHex } from \"viem\";\n\n/** Prefix every account key carries; the opaque hex part follows it. */\nexport const AK_PREFIX = \"ak_\";\n/** Bytes of cryptographic randomness behind an account key (→ 64 hex chars). */\nexport const AK_RANDOM_BYTES = 32;\n\n// `ak_` + 64 lowercase hex chars (= AK_RANDOM_BYTES * 2).\nconst AK_FORMAT_RE = /^ak_[0-9a-f]{64}$/;\n\n/**\n * Mint a fresh account key: `ak_` followed by the hex of AK_RANDOM_BYTES\n * cryptographically-random bytes. This is the bearer secret — knowing it IS the\n * capability to read/write the account. Persist it securely (e.g. a keystore);\n * the worker stores only its hash, so a lost key is unrecoverable.\n */\nexport function generateAccountKey(): string {\n const bytes = crypto.getRandomValues(new Uint8Array(AK_RANDOM_BYTES));\n // viem's bytesToHex is 0x-prefixed lowercase; the account-key format carries no\n // 0x prefix, so drop it (`ak_` + 64 lowercase hex).\n return AK_PREFIX + bytesToHex(bytes).slice(2);\n}\n\n/** True iff `s` is a string in the canonical `ak_<64 lowercase hex>` shape. */\nexport function isAccountKeyFormat(s: unknown): s is string {\n return typeof s === \"string\" && AK_FORMAT_RE.test(s);\n}\n","// client/src/crypto.ts\nimport { hkdf } from \"@noble/hashes/hkdf\";\nimport { hmac } from \"@noble/hashes/hmac\";\nimport { sha256 } from \"@noble/hashes/sha2\";\nimport { hexToBytes } from \"viem\";\n\nconst KEY_LENGTH = 32;\nconst IV_LENGTH = 12;\nconst TAG_LENGTH = 16; // AES-GCM authentication tag\n\nconst utf8 = (s: string) => new TextEncoder().encode(s);\n\n// --- Versioned, self-describing envelope --------------------------------------------\n// A stored blob is `base64( magic ‖ ver ‖ suite ‖ kdf_id ‖ IV ‖ ct+tag )`. The 5-byte\n// header — and, for VALUES, the key's blind-index digest — is bound into the AES-GCM AAD,\n// so (a) the scheme ids cannot be stripped or downgraded, and (b) a value encrypted for one\n// key cannot be substituted by the server for another key's request: tampering any bound\n// byte, or decrypting under a different key's digest, fails the authentication tag.\nconst MAGIC0 = 0x61; // 'a'\nconst MAGIC1 = 0x6b; // 'k'\nexport const ENVELOPE_VER = 0x01;\nexport const SUITE_AES256GCM = 0x01;\nexport const KDF_V1 = 0x01; // domain-separated HKDF (salt agentkv/v1/*)\nexport const DIGEST_SCHEME_V1 = 0x01;\nconst HEADER = Uint8Array.of(MAGIC0, MAGIC1, ENVELOPE_VER, SUITE_AES256GCM, KDF_V1);\nconst HEADER_LEN = HEADER.length; // 5\n\n/** Validate/normalize a supplied 32-byte encryption key (used directly as the AES key). */\nexport function normalizeEncryptionKey(key: Uint8Array | `0x${string}`): Uint8Array {\n const bytes = typeof key === \"string\" ? hexToBytes(key) : key;\n if (bytes.length !== KEY_LENGTH) {\n throw new Error(`encryption key must be 32 bytes, got ${bytes.length}`);\n }\n return bytes;\n}\n\n/**\n * All keys derived from one per-wallet `ikm` with domain-separated HKDF labels — so the\n * value key, the key-name key, and the blind-index MAC key are independent.\n */\nexport interface KeyMaterial {\n value: Uint8Array; // AES key for values (kdf_id=1)\n keyName: Uint8Array; // AES key for the encrypted key-name\n mac: Uint8Array; // HMAC key for the blind-index digest\n}\n\nexport function deriveKeyMaterial(ikm: Uint8Array): KeyMaterial {\n return {\n value: hkdf(sha256, ikm, utf8(\"agentkv/v1/enc\"), utf8(\"value\"), KEY_LENGTH),\n keyName: hkdf(sha256, ikm, utf8(\"agentkv/v1/enc\"), utf8(\"keyname\"), KEY_LENGTH),\n mac: hkdf(sha256, ikm, utf8(\"agentkv/v1/mac\"), utf8(\"lookup\"), KEY_LENGTH),\n };\n}\n\n/**\n * Blind-index digest for a key NAME: `scheme_tag ‖ HMAC-SHA256(macKey, NFC(name))`,\n * url-safe-base64. Deterministic and per-wallet (macKey is per-wallet), so the server\n * looks up by an opaque token it cannot invert or correlate across wallets. NFC is the\n * frozen normalization (case-sensitive, whitespace-preserving). Used by set/get/delete/list\n * to look a value up under an opaque per-wallet token the server cannot invert.\n */\nexport function hashKey(macKey: Uint8Array, name: string): string {\n const mac = hmac(sha256, macKey, utf8(name.normalize(\"NFC\")));\n const tagged = new Uint8Array(1 + mac.length);\n tagged[0] = DIGEST_SCHEME_V1;\n tagged.set(mac, 1);\n // MUST stay URL-safe (base64url: [A-Za-z0-9_-], no padding) — kvRoute() embeds this\n // digest directly in the request path without encodeURIComponent.\n return toBase64Url(tagged);\n}\n\nfunction toBase64(bytes: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);\n return btoa(binary); // available in Node 18+, browsers, Workers\n}\n\nfunction toBase64Url(bytes: Uint8Array): string {\n return toBase64(bytes).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nfunction fromBase64(b64: string): Uint8Array {\n const binary = atob(b64);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);\n return out;\n}\n\n/** AAD = the stored 5-byte header, plus (for values) the key's blind-index digest. */\nfunction aadBytes(aad?: string): Uint8Array<ArrayBuffer> {\n if (!aad) return HEADER;\n const extra = utf8(aad);\n const out = new Uint8Array(HEADER_LEN + extra.length);\n out.set(HEADER, 0);\n out.set(extra, HEADER_LEN);\n return out;\n}\n\nasync function importAesKey(key: Uint8Array): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n \"raw\",\n key.buffer.slice(key.byteOffset, key.byteOffset + key.byteLength) as ArrayBuffer,\n { name: \"AES-GCM\" },\n false,\n [\"encrypt\", \"decrypt\"],\n );\n}\n\nasync function gcmDecrypt(\n key: Uint8Array,\n iv: Uint8Array<ArrayBuffer>,\n ct: Uint8Array<ArrayBuffer>,\n aad: Uint8Array<ArrayBuffer>,\n): Promise<string> {\n const cryptoKey = await importAesKey(key);\n const plaintext = await crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv, additionalData: aad },\n cryptoKey,\n ct,\n );\n return new TextDecoder().decode(plaintext);\n}\n\n/**\n * Encrypt plaintext with AES-256-GCM, emitting the versioned self-describing envelope\n * `base64( magic ‖ ver ‖ suite ‖ kdf_id ‖ IV ‖ ct+tag )`. The header (and, when `aad` is\n * supplied, the key's digest) is bound into the AAD.\n */\nexport async function encrypt(key: Uint8Array, plaintext: string, aad?: string): Promise<string> {\n const cryptoKey = await importAesKey(key);\n const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH));\n const data = utf8(plaintext);\n const ct = new Uint8Array(\n await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv, additionalData: aadBytes(aad) },\n cryptoKey,\n data,\n ),\n );\n const packed = new Uint8Array(HEADER_LEN + iv.length + ct.length);\n packed.set(HEADER, 0);\n packed.set(iv, HEADER_LEN);\n packed.set(ct, HEADER_LEN + iv.length);\n return toBase64(packed);\n}\n\n/**\n * Decrypt a value produced by encrypt(). Requires the versioned envelope (magic + known\n * ver/suite/kdf) and verifies the AAD-bound header (and, when `aad` is supplied, the key's\n * digest — so a value the server serves for the WRONG key fails the tag instead of decrypting).\n */\nexport async function decrypt(key: Uint8Array, packed: string, aad?: string): Promise<string> {\n const bytes = fromBase64(packed);\n const known =\n bytes.length >= HEADER_LEN + IV_LENGTH + TAG_LENGTH &&\n bytes[0] === MAGIC0 &&\n bytes[1] === MAGIC1 &&\n bytes[2] === ENVELOPE_VER &&\n bytes[3] === SUITE_AES256GCM &&\n bytes[4] === KDF_V1;\n if (!known) {\n throw new Error(\n `decryption failed: not a recognized AgentKV envelope (ver=${bytes[2]}, suite=${bytes[3]}, ` +\n `kdf=${bytes[4]}) — upgrade @agentkv/client, or the encryption key is wrong / the blob is corrupted`,\n );\n }\n try {\n return await gcmDecrypt(\n key,\n bytes.slice(HEADER_LEN, HEADER_LEN + IV_LENGTH),\n bytes.slice(HEADER_LEN + IV_LENGTH),\n aadBytes(aad),\n );\n } catch {\n throw new Error(\n \"decryption failed: wrong encryption key, corrupted blob, or a value served for a different key\",\n );\n }\n}\n","// client/src/payment.ts\n//\n// The payment/auth-header building plumbing that lived here now lives in\n// `@agentx402-ai/core` (a future second service package reuses it too). This file\n// is now a back-compat re-export shim under the SAME names so existing\n// `from \"./payment\"` imports (and client/test/payment.test.ts's back-compat\n// smoke test) keep resolving unchanged. The deep test coverage for this logic\n// now lives in `core/test/payment.test.ts`.\n\nexport type { IdentityHeaders } from \"@agentx402-ai/core\";\nexport {\n buildBearerHeaders,\n buildIdentityHeaders,\n buildPaymentHeader,\n challengePriceUsd,\n decodeBase64Utf8,\n freshNonce,\n nonceFromIdempotencyKey,\n nowSec,\n} from \"@agentx402-ai/core\";\n","// client/src/types.ts\n//\n// `AgentKVError` (base error class), `SpendCapError`, `Signer`,\n// `UsageBlock`, and the EIP-712/CAIP-2 domain constants live in\n// `@agentx402-ai/core`. Re-exported here under the SAME names for back-compat —\n// `@agentkv/client` depends on and re-exports core's class/values, it never\n// re-declares them, so `err instanceof AgentKVError` keeps working for\n// anything caught across this package boundary (see core/src/errors.ts and\n// core/test/errors.test.ts + client/test/errors.test.ts for the guarantee).\n\nexport type { Signer, UsageBlock } from \"@agentx402-ai/core\";\nexport {\n AgentKVError,\n chainIdFromCaip2,\n EIP712_DOMAIN_NAME,\n EIP712_DOMAIN_VERSION,\n SpendCapError,\n} from \"@agentx402-ai/core\";\n\nimport type { Signer, UsageBlock } from \"@agentx402-ai/core\";\n\n/** Request passed to `topoffPayer` when the client needs an account-key top-off. */\nexport interface TopoffPayerRequest {\n /** `${endpoint}/account/deposit` — the ONLY URL a hook should ever pay. */\n depositUrl: string;\n /** The `ak_…` bearer that must authorize the deposit (`Authorization: Bearer <ak>`). */\n accountKey: string;\n /** Requested top-off in USD (= `prepay.topoff`, >= $1 — the server minimum). */\n amountUsd: number;\n /** Hard payment ceiling in atomic USDC units (1e6 = $1) the hook MUST enforce (e.g. awal `--max-amount`). */\n maxAmountAtomic: number;\n}\n\n/**\n * Request handed to `opInlinePayer`: the WHOLE encrypted account-key `set`/`get`\n * op, ready to send as-is. The hook drives its OWN 402→pay→retry (e.g. via\n * `awal x402 pay`) and returns the final response — the client never sees the\n * intermediate 402 on this path.\n */\nexport interface OpInlineRequest {\n /** `${endpoint}/kv/<digest>` — the ONLY URL a hook should ever request. */\n url: string;\n method: \"POST\" | \"GET\";\n /** Ciphertext JSON body (POST only; omitted on GET). */\n body?: string;\n /** Full request headers: bearer `Authorization`, `Idempotency-Key`, `content-type` (POST). */\n headers: Record<string, string>;\n /** Hard payment ceiling in atomic USDC units (1e6 = $1) the hook MUST enforce (e.g. awal `--max-amount`). */\n maxAmountAtomic: number;\n}\n\n/** Final response from `opInlinePayer`, after its own 402→pay→retry has settled. */\nexport interface OpInlineResponse {\n status: number;\n body: string;\n headers: Record<string, string>;\n}\n\ninterface AgentKVCommon {\n endpoint: string;\n network?: string;\n /** Per-paying-call USD cap; throws SpendCapError if exceeded. */\n maxSpendUsd?: number;\n /**\n * Cumulative USD cap across this client instance. **Best-effort**: the running\n * total is a plain in-memory counter, so concurrent paying calls can race\n * (both pass `assertSpend` before either `recordSpend`s) and modestly overshoot\n * the cap. It is a guardrail, not a hard ledger — serialize paying calls if you\n * need a strict bound.\n */\n maxSessionSpendUsd?: number;\n /**\n * Bounded internal retries on TRANSIENT failures (a thrown fetch / lost\n * response, or a 5xx). Default 2 (3 attempts total). Retries reuse the op's\n * stable Idempotency-Key (and pinned EIP-3009 nonce on paid ops) so a\n * processed-but-unacked request dedupes server-side instead of double-charging.\n * Kept small so a re-sent paid authorization stays within validBefore. Set 0 to disable.\n */\n retries?: number;\n /** Per-attempt request timeout in ms (aborts a hung-open connection so an op can't wedge forever). Default 30000. Pass 0 to disable. */\n timeoutMs?: number;\n /** Injectable `fetch` for proxies / instrumentation / testing. Defaults to the global `fetch`. */\n fetch?: typeof fetch;\n /** Opt-in Discounted Prepay. When set, the client keeps a credit balance topped up. */\n prepay?: {\n /** Top off when the tracked balance (USD) falls below this. */\n watermark: number;\n /** Top-off amount in USD (>= 1). */\n topoff: number;\n /** Opt-in background top-off instead of synchronous single-shot. Default false. */\n async?: boolean;\n };\n /**\n * ACCOUNT-KEY MODE ONLY: pays a prepaid-credit top-off to\n * `${endpoint}/account/deposit` on this client's behalf (a managed account has\n * no signing wallet). Called single-flight when the tracked balance falls below\n * `prepay.watermark`, and as a fallback when an op hits an insufficient-credits\n * 402. Resolve only once the deposit has SETTLED; a rejection surfaces as\n * `account_topoff_failed` (fatal on the hard-402 path, swallowed on the\n * proactive path). REQUIRED alongside `prepay` in account-key mode; rejected\n * (`invalid_config`) in wallet mode or without `prepay` (it could never fire).\n */\n topoffPayer?: (req: TopoffPayerRequest) => Promise<void>;\n /**\n * ACCOUNT-KEY MODE ONLY: routes a WHOLE `set`/`get` op through an external inline\n * x402 transport (e.g. `awal x402 pay`) instead of the stage-1 deposit top-off —\n * opt-in, pay-per-op, no `prepay` required. Called on a hard 402 (insufficient\n * credits): the client hands the hook the complete encrypted request\n * (`{url, method, body, headers}`, already bearer-authenticated and\n * Idempotency-Keyed) and the hook drives its OWN 402→pay→retry, returning the\n * final `{status, body, headers}` for the client to parse/decrypt as usual.\n *\n * Mutually exclusive with `topoffPayer` PER OP: when BOTH are configured,\n * `topoffPayer` (deposit top-off) always takes precedence and `opInlinePayer`\n * is used only when `topoffPayer` is absent — see client/README.md. Rejected\n * (`invalid_config`) in wallet mode (a signing wallet pays its own challenges).\n */\n opInlinePayer?: (req: OpInlineRequest) => Promise<OpInlineResponse>;\n}\n\n/**\n * Construct with one of four auth shapes:\n * - `{ privateKey }` — a raw wallet key (HKDF-derives the AES key from the KEY BYTES;\n * wallet signs).\n * - `{ signer, encryptionKey }` — a wallet signer + an explicit AES key.\n * - `{ signer }` — a wallet signer; the AES key is SIGN-TO-DERIVED (HKDF over the wallet's\n * signature of a fixed message).\n * - `{ accountKey, encryptionKey }` — account-key mode: a managed account with\n * NO signing wallet. Auth is an opaque `ak_…` bearer token (server hashes it to\n * name storage + debits prepaid credits — no x402/EIP-712). There is no wallet\n * to derive an AES key from, so an explicit `encryptionKey` is REQUIRED.\n *\n * ⚠️ Encryption-key stability — the shapes are NOT interchangeable for the same wallet:\n * - `{ privateKey: k }` and `{ signer: accountFrom(k) }` (sign-to-derive) derive DIFFERENT\n * encryption keys (raw key bytes vs a signature over them). Switching a wallet between\n * these two shapes changes the value/key-name/blind-index keys, so previously written\n * data becomes unreadable AND unlisted (its lookup digests no longer match) with no error\n * — reads simply return null. To move between them, or to use a KMS/hardware/MPC signer,\n * pin an explicit `encryptionKey` so the AES key never depends on the signer shape.\n * - Sign-to-derive requires a DETERMINISTIC signer whose signTypedData returns the standard\n * 65-byte ECDSA form. Non-deterministic (some MPC/threshold) or alternate-encoding\n * signers (EIP-2098 compact, ERC-1271/6492 smart accounts) either rotate the key per call\n * or are rejected — those MUST pass an explicit `encryptionKey`.\n * - The sign-to-derive signature IS secret-grade material: whoever obtains it can derive all\n * of this wallet's AgentKV keys. Treat it like a private key (do not log / expose it).\n */\nexport type AgentKVOptions = AgentKVCommon &\n (\n | { privateKey: `0x${string}` }\n | { signer: Signer; encryptionKey: Uint8Array | `0x${string}` }\n | { signer: Signer }\n | { accountKey: string; encryptionKey: Uint8Array | `0x${string}` }\n );\n\n/** Per-write options. */\nexport interface SetOptions {\n /** Time-to-live in days (server default 90). */\n ttlDays?: number;\n /** If true, reads do not extend expiry (server default false). */\n strictTtl?: boolean;\n /**\n * Stable key identifying this logical write, reused across retries so a\n * retried set() (after a crash/timeout) is exactly-once: the server hits its\n * idempotency record instead of charging again. Defaults to a fresh value\n * (each call is a distinct write).\n */\n idempotencyKey?: string;\n}\n\n/** Per-read options. */\nexport interface GetOptions {\n /**\n * Stable key making a retried get() exactly-once: the same key hits the\n * server's read idempotency record (credit path) and pins the EIP-3009 nonce\n * (paid path) instead of charging again. Defaults to a fresh value per call\n * (each get() is a distinct, separately-charged read).\n */\n idempotencyKey?: string;\n}\n\n/** Result of a successful write. */\nexport interface SetResult {\n ok: true;\n expires_at: string;\n /** Machine-readable usage envelope for this write. Absent only if the server predates it. */\n usage?: UsageBlock;\n}\n\n/** Result of a successful delete. */\nexport interface DeleteResult {\n ok: true;\n}\n\n/** Result of a successful credit deposit. */\nexport interface DepositResult {\n credits_added: number;\n balance: number;\n}\n\n/** Standard AgentKV error response body. */\nexport interface ErrorBody {\n error: string;\n code: string;\n hint?: string;\n}\n\n/** CAIP-2 network used when none is supplied (Base mainnet). */\nexport const DEFAULT_NETWORK = \"eip155:8453\";\n"],"mappings":";AAGA,SAAS,sBAAsB;AAC/B,SAAS,cAAAA,mBAAkB;AAC3B,SAAS,2BAA2B;;;ACQpC,SAAS,kBAAkB;AAGpB,IAAM,YAAY;AAElB,IAAM,kBAAkB;AAG/B,IAAM,eAAe;AAQd,SAAS,qBAA6B;AAC3C,QAAM,QAAQ,OAAO,gBAAgB,IAAI,WAAW,eAAe,CAAC;AAGpE,SAAO,YAAY,WAAW,KAAK,EAAE,MAAM,CAAC;AAC9C;AAGO,SAAS,mBAAmB,GAAyB;AAC1D,SAAO,OAAO,MAAM,YAAY,aAAa,KAAK,CAAC;AACrD;;;ACtCA,SAAS,YAAY;AACrB,SAAS,YAAY;AACrB,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAE3B,IAAM,aAAa;AACnB,IAAM,YAAY;AAClB,IAAM,aAAa;AAEnB,IAAM,OAAO,CAAC,MAAc,IAAI,YAAY,EAAE,OAAO,CAAC;AAQtD,IAAM,SAAS;AACf,IAAM,SAAS;AACR,IAAM,eAAe;AACrB,IAAM,kBAAkB;AACxB,IAAM,SAAS;AACf,IAAM,mBAAmB;AAChC,IAAM,SAAS,WAAW,GAAG,QAAQ,QAAQ,cAAc,iBAAiB,MAAM;AAClF,IAAM,aAAa,OAAO;AAGnB,SAAS,uBAAuB,KAA6C;AAClF,QAAM,QAAQ,OAAO,QAAQ,WAAW,WAAW,GAAG,IAAI;AAC1D,MAAI,MAAM,WAAW,YAAY;AAC/B,UAAM,IAAI,MAAM,wCAAwC,MAAM,MAAM,EAAE;AAAA,EACxE;AACA,SAAO;AACT;AAYO,SAAS,kBAAkB,KAA8B;AAC9D,SAAO;AAAA,IACL,OAAO,KAAK,QAAQ,KAAK,KAAK,gBAAgB,GAAG,KAAK,OAAO,GAAG,UAAU;AAAA,IAC1E,SAAS,KAAK,QAAQ,KAAK,KAAK,gBAAgB,GAAG,KAAK,SAAS,GAAG,UAAU;AAAA,IAC9E,KAAK,KAAK,QAAQ,KAAK,KAAK,gBAAgB,GAAG,KAAK,QAAQ,GAAG,UAAU;AAAA,EAC3E;AACF;AASO,SAAS,QAAQ,QAAoB,MAAsB;AAChE,QAAM,MAAM,KAAK,QAAQ,QAAQ,KAAK,KAAK,UAAU,KAAK,CAAC,CAAC;AAC5D,QAAM,SAAS,IAAI,WAAW,IAAI,IAAI,MAAM;AAC5C,SAAO,CAAC,IAAI;AACZ,SAAO,IAAI,KAAK,CAAC;AAGjB,SAAO,YAAY,MAAM;AAC3B;AAEA,SAAS,SAAS,OAA2B;AAC3C,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,IAAK,WAAU,OAAO,aAAa,MAAM,CAAC,CAAC;AAC7E,SAAO,KAAK,MAAM;AACpB;AAEA,SAAS,YAAY,OAA2B;AAC9C,SAAO,SAAS,KAAK,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AAClF;AAEA,SAAS,WAAW,KAAyB;AAC3C,QAAM,SAAS,KAAK,GAAG;AACvB,QAAM,MAAM,IAAI,WAAW,OAAO,MAAM;AACxC,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,IAAK,KAAI,CAAC,IAAI,OAAO,WAAW,CAAC;AACpE,SAAO;AACT;AAGA,SAAS,SAAS,KAAuC;AACvD,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,QAAQ,KAAK,GAAG;AACtB,QAAM,MAAM,IAAI,WAAW,aAAa,MAAM,MAAM;AACpD,MAAI,IAAI,QAAQ,CAAC;AACjB,MAAI,IAAI,OAAO,UAAU;AACzB,SAAO;AACT;AAEA,eAAe,aAAa,KAAqC;AAC/D,SAAO,OAAO,OAAO;AAAA,IACnB;AAAA,IACA,IAAI,OAAO,MAAM,IAAI,YAAY,IAAI,aAAa,IAAI,UAAU;AAAA,IAChE,EAAE,MAAM,UAAU;AAAA,IAClB;AAAA,IACA,CAAC,WAAW,SAAS;AAAA,EACvB;AACF;AAEA,eAAe,WACb,KACA,IACA,IACA,KACiB;AACjB,QAAM,YAAY,MAAM,aAAa,GAAG;AACxC,QAAM,YAAY,MAAM,OAAO,OAAO;AAAA,IACpC,EAAE,MAAM,WAAW,IAAI,gBAAgB,IAAI;AAAA,IAC3C;AAAA,IACA;AAAA,EACF;AACA,SAAO,IAAI,YAAY,EAAE,OAAO,SAAS;AAC3C;AAOA,eAAsB,QAAQ,KAAiB,WAAmB,KAA+B;AAC/F,QAAM,YAAY,MAAM,aAAa,GAAG;AACxC,QAAM,KAAK,OAAO,gBAAgB,IAAI,WAAW,SAAS,CAAC;AAC3D,QAAM,OAAO,KAAK,SAAS;AAC3B,QAAM,KAAK,IAAI;AAAA,IACb,MAAM,OAAO,OAAO;AAAA,MAClB,EAAE,MAAM,WAAW,IAAI,gBAAgB,SAAS,GAAG,EAAE;AAAA,MACrD;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,QAAM,SAAS,IAAI,WAAW,aAAa,GAAG,SAAS,GAAG,MAAM;AAChE,SAAO,IAAI,QAAQ,CAAC;AACpB,SAAO,IAAI,IAAI,UAAU;AACzB,SAAO,IAAI,IAAI,aAAa,GAAG,MAAM;AACrC,SAAO,SAAS,MAAM;AACxB;AAOA,eAAsB,QAAQ,KAAiB,QAAgB,KAA+B;AAC5F,QAAM,QAAQ,WAAW,MAAM;AAC/B,QAAM,QACJ,MAAM,UAAU,aAAa,YAAY,cACzC,MAAM,CAAC,MAAM,UACb,MAAM,CAAC,MAAM,UACb,MAAM,CAAC,MAAM,gBACb,MAAM,CAAC,MAAM,mBACb,MAAM,CAAC,MAAM;AACf,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR,6DAA6D,MAAM,CAAC,CAAC,WAAW,MAAM,CAAC,CAAC,SAC/E,MAAM,CAAC,CAAC;AAAA,IACnB;AAAA,EACF;AACA,MAAI;AACF,WAAO,MAAM;AAAA,MACX;AAAA,MACA,MAAM,MAAM,YAAY,aAAa,SAAS;AAAA,MAC9C,MAAM,MAAM,aAAa,SAAS;AAAA,MAClC,SAAS,GAAG;AAAA,IACd;AAAA,EACF,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;;;ACxKA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACRP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AA8LA,IAAM,kBAAkB;;;AJ9MxB,IAAM,UAAU;AA+DvB,IAAM,KAAK;AAUX,IAAM,wBAAwB,EAAE,MAAM,sBAAsB,SAAS,IAAI;AACzE,IAAM,uBAAuB;AAAA,EAC3B,QAAQ;AAAA,IACN,EAAE,MAAM,WAAW,MAAM,SAAS;AAAA,IAClC,EAAE,MAAM,WAAW,MAAM,SAAS;AAAA,EACpC;AACF;AACA,IAAM,yBAAyB,EAAE,SAAS,kBAAkB,SAAS,KAAK;AAUnE,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AACzB,IAAM,oBAAoB;AAS1B,IAAM,qBAAqB;AASlC,SAAS,iBAAiB,WAAkC;AAC1D,MAAI,CAAC,OAAO,SAAS,SAAS,EAAG,QAAO;AACxC,QAAM,SAAS,KAAK,MAAM,YAAY,GAAS;AAC/C,MAAI,CAAC,OAAO,UAAU,MAAM,KAAK,UAAU,EAAG,QAAO;AACrD,MAAI,KAAK,IAAI,YAAY,MAAY,MAAM,IAAI,SAAS,KAAM,QAAO;AACrE,SAAO;AACT;AAEO,IAAM,UAAN,MAAc;AAAA;AAAA;AAAA;AAAA;AAAA,EAKV;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACQ;AAAA,EACA;AAAA,EACT;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAkB;AAAA;AAAA,EAGT;AAAA;AAAA,EAEA,eAAuB;AAAA;AAAA,EAEvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA;AAAA;AAAA,EAET;AAAA;AAAA,EAEA,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKjB;AAAA;AAAA,EAEA;AAAA,EAER,YAAY,MAAsB;AAChC,SAAK,WAAW,KAAK,SAAS,QAAQ,QAAQ,EAAE;AAChD,SAAK,UAAU,KAAK,WAAW;AAC/B,SAAK,cAAc,KAAK;AACxB,SAAK,qBAAqB,KAAK;AAC/B,SAAK,aAAa,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,WAAW,CAAC,CAAC;AAC3D,SAAK,YAAY,KAAK;AACtB,SAAK,YAAY,KAAK;AAGtB,UAAM,gBAAgB,gBAAgB,QAAQ,KAAK,cAAc;AACjE,QAAI,KAAK,gBAAgB,QAAW;AAClC,UAAI,OAAO,KAAK,gBAAgB,YAAY;AAC1C,cAAM,IAAI,aAAa,kCAAkC,kBAAkB,CAAC;AAAA,MAC9E;AACA,UAAI,CAAC,eAAe;AAGlB,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,UAAI,CAAC,KAAK,QAAQ;AAGhB,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,WAAK,cAAc,KAAK;AAAA,IAC1B;AACA,QAAI,KAAK,kBAAkB,QAAW;AACpC,UAAI,OAAO,KAAK,kBAAkB,YAAY;AAC5C,cAAM,IAAI,aAAa,oCAAoC,kBAAkB,CAAC;AAAA,MAChF;AACA,UAAI,CAAC,eAAe;AAGlB,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAGA,WAAK,gBAAgB,KAAK;AAAA,IAC5B;AACA,QAAI,KAAK,QAAQ;AACf,UAAI,iBAAiB,CAAC,KAAK,aAAa;AAItC,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAIA,YAAM,eAAe,iBAAiB,KAAK,OAAO,MAAM;AACxD,UAAI,iBAAiB,QAAQ,EAAE,KAAK,OAAO,UAAU,IAAI;AACvD,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,UAAI,EAAE,KAAK,OAAO,aAAa,IAAI;AACjC,cAAM,IAAI,aAAa,iCAAiC,kBAAkB,CAAC;AAAA,MAC7E;AACA,WAAK,SAAS,KAAK;AACnB,WAAK,eAAe;AAAA,IACtB;AAMA,QAAI,eAAe;AAKjB,UAAI,CAAC,mBAAmB,KAAK,UAAU,GAAG;AACxC,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,UAAI,CAAC,KAAK,eAAe;AACvB,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,WAAK,aAAa,KAAK;AACvB,WAAK,SAAS;AAGd,WAAK,UAAU;AACf,WAAK,OAAO,uBAAuB,KAAK,aAAa;AAAA,IACvD,WAAW,gBAAgB,QAAQ,KAAK,cAAc,MAAM;AAK1D,UAAI,mBAAmB,QAAQ,KAAK,eAAe;AAIjD,cAAM,IAAI;AAAA,UACR;AAAA,UAEA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,WAAK,SAAS,oBAAoB,KAAK,UAAU;AACjD,WAAK,OAAOC,YAAW,KAAK,UAAU;AACtC,WAAK,UAAU,KAAK,OAAO;AAAA,IAC7B,WAAW,YAAY,QAAQ,KAAK,UAAU,MAAM;AAClD,WAAK,SAAS,KAAK;AACnB,UAAI,mBAAmB,QAAQ,KAAK,eAAe;AACjD,aAAK,OAAO,uBAAuB,KAAK,aAAa;AAAA,MACvD;AAEA,WAAK,UAAU,KAAK,OAAO;AAAA,IAC7B,OAAO;AAKL,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,iBAAuC;AAC7C,QAAI,KAAK,IAAK,QAAO,QAAQ,QAAQ,KAAK,GAAG;AAC7C,QAAI,CAAC,KAAK,YAAY;AACpB,WAAK,cAAc,YAAY;AAC7B,YAAI,MAAM,KAAK;AACf,YAAI,CAAC,KAAK;AAIR,cAAI,CAAC,KAAK,QAAQ;AAChB,kBAAM,IAAI;AAAA,cACR;AAAA,cACA;AAAA,cACA;AAAA,YACF;AAAA,UACF;AACA,gBAAM,MAAM,MAAM,KAAK,OAAO,cAAc;AAAA,YAC1C,QAAQ;AAAA,YACR,OAAO;AAAA,YACP,aAAa;AAAA,YACb,SAAS;AAAA,UACX,CAAC;AACD,gBAAM,WAAWA,YAAW,GAAG;AAM/B,cAAI,SAAS,WAAW,IAAI;AAC1B,kBAAM,IAAI;AAAA,cACR,+DAA+D,SAAS,MAAM;AAAA,cAE9E;AAAA,cACA;AAAA,YACF;AAAA,UACF;AACA,gBAAM;AAAA,QACR;AACA,cAAM,KAAK,kBAAkB,GAAG;AAChC,aAAK,MAAM;AACX,eAAO;AAAA,MACT,GAAG,EAAE,MAAM,CAAC,QAAQ;AAIlB,aAAK,aAAa;AAClB,cAAM;AAAA,MACR,CAAC;AAAA,IACH;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,aAAa,QAAgB,KAA8B;AACvE,UAAM,KAAK,MAAM,KAAK,eAAe;AAGrC,WAAO,QAAQ,GAAG,OAAO,QAAQ,QAAQ,GAAG,KAAK,GAAG,CAAC;AAAA,EACvD;AAAA,EAEQ,YAAY,KAAa,OAAqC,CAAC,GAAS;AAI9E,QAAI,CAAC,KAAK,kBAAkB,KAAK,gBAAgB,UAAa,MAAM,KAAK,aAAa;AACpF,YAAM,IAAI,cAAc,UAAU,GAAG,0BAA0B,KAAK,WAAW,EAAE;AAAA,IACnF;AACA,QACE,KAAK,uBAAuB,UAC5B,KAAK,kBAAkB,MAAM,KAAK,oBAClC;AACA,YAAM,IAAI;AAAA,QACR,UAAU,GAAG,8BAA8B,KAAK,kBAAkB,YAAY,KAAK,eAAe;AAAA,MACpG;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,YAAY,KAAmB;AACrC,SAAK,mBAAmB;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,qBAAqB,KAAmB;AAC9C,QAAI,KAAK,gBAAgB,UAAa,MAAM,oBAAoB;AAC9D,YAAM,IAAI;AAAA,QACR,2BAA2B,GAAG,0BAA0B,kBAAkB;AAAA,MAE5E;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,qBAA6B;AACnC,WAAO,KAAK,eAAe;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBQ,cAAc,KAAuB;AAC3C,UAAM,SAAS,IAAI,QAAQ,IAAI,kBAAkB;AACjD,QAAI,CAAC,OAAQ,QAAO;AACpB,QAAI;AAEF,YAAM,SAAS,KAAK,MAAM,iBAAiB,MAAM,CAAC;AAClD,aAAO,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS;AAAA,IAC7D,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBQ,eACN,KACA,OACmB;AACnB,WAAO,eAAe,KAAK,OAAO,KAAK,YAAY;AAAA,MACjD,WAAW,KAAK;AAAA,MAChB,WAAW,KAAK;AAAA,IAClB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,aAAa,KAAqB;AACxC,UAAM,UAAU,IAAI,QAAQ,IAAI,6BAA6B;AAC7D,QAAI,YAAY,QAAQ,YAAY,IAAI;AACtC,YAAM,IAAI,OAAO,OAAO;AACxB,UAAI,OAAO,SAAS,CAAC,EAAG,MAAK,eAAe;AAAA,IAC9C;AACA,UAAM,YAAY,IAAI,QAAQ,IAAI,kBAAkB;AACpD,QAAI,UAAW,MAAK,oBAAoB;AAAA,EAC1C;AAAA;AAAA;AAAA,EAIQ,mBAA2B;AACjC,WAAO,KAAK,MAAM,KAAK,OAAQ,YAAY,gBAAgB;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,iBAA0B;AAChC,QACE,CAAC,KAAK,UACN,KAAK,OAAO,SACZ,KAAK,kBACL,KAAK,iBAAiB,UACtB,KAAK,gBAAgB,KAAK,iBAAiB,GAC3C;AACA,aAAO;AAAA,IACT;AACA,SAAK,iBAAiB;AACtB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,wBAAiC;AACvC,QAAI,CAAC,KAAK,UAAU,KAAK,OAAO,SAAS,KAAK,eAAgB,QAAO;AACrE,SAAK,iBAAiB;AACtB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,kBAAiC;AAC7C,UAAM,IAAI,KAAK,iBAAiB;AAChC,SAAK,gBAAgB;AACrB,QAAI;AACF,YAAM;AAAA,IACR,UAAE;AACA,UAAI,KAAK,kBAAkB,EAAG,MAAK,gBAAgB;AAAA,IACrD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBQ,mBAAyB;AAC/B,QACE,CAAC,KAAK,QAAQ,SACd,KAAK,kBACL,KAAK,iBAAiB,UACtB,KAAK,gBAAgB,KAAK,iBAAiB,GAC3C;AACA;AAAA,IACF;AAGA,QACE,KAAK,uBAAuB,UAC5B,KAAK,kBAAkB,KAAK,OAAO,SAAS,KAAK,oBACjD;AACA;AAAA,IACF;AACA,SAAK,iBAAiB;AACtB,QAAI,KAAK,YAAY;AAInB,WAAK,KAAK,iBAAiB,EACxB,MAAM,MAAM;AAAA,MAAC,CAAC,EACd,QAAQ,MAAM;AACb,aAAK,iBAAiB;AAAA,MACxB,CAAC;AACH;AAAA,IACF;AAKA,SAAK,KAAK,WAAW,KAAK,OAAO,QAAQ,EAAE,gBAAgB,KAAK,CAAC,EAC9D,MAAM,MAAM;AAAA,IAAC,CAAC,EACd,QAAQ,MAAM;AACb,WAAK,iBAAiB;AAAA,IACxB,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,uBAAgC;AACtC,QAAI,KAAK,uBAAuB,OAAW,QAAO;AAClD,WAAO,KAAK,kBAAkB,KAAK,OAAQ,UAAU,KAAK;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,MAAM,MAGZ;AACA,UAAM,OAAO,KAAK,aAAa,GAAG,EAAE,GAAG,KAAK,IAAI;AAChD,UAAM,IAAI,KAAK,QAAQ,IAAI,KAAK,KAAK,KAAK;AAC1C,WAAO,EAAE,MAAM,KAAK,GAAG,KAAK,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA,EAKQ,QAAQ,QAA+C;AAC7D,WAAO,KAAK,MAAM,EAAE,MAAM,OAAO,MAAM,GAAG,CAAC;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,YAAY,QAAgB,MAA+C;AACvF,QAAI,KAAK,WAAY,QAAO,mBAAmB,KAAK,UAAU;AAC9D,WAAO,EAAE,GAAI,MAAM,KAAK,gBAAgB,QAAQ,IAAI,EAAG;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,gBAAwB;AAC9B,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGQ,gBAAgB,QAAgB,MAAc;AACpD,QAAI,CAAC,KAAK,QAAQ;AAGhB,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,WAAO,qBAAqB,KAAK,QAAQ;AAAA,MACvC;AAAA,MACA;AAAA,MACA,MAAM,IAAI,IAAI,KAAK,QAAQ,EAAE;AAAA,MAC7B,SAAS,KAAK;AAAA,IAChB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAc,UACZ,QACA,MAaY;AACZ,UAAM,EAAE,MAAM,KAAK,gBAAgB,eAAe,MAAM,IAAI;AAI5D,QAAI,KAAK,YAAY;AAGnB,UAAI,YAAY;AAKhB,UAAI,OAAO,WAAW,KAAK,eAAe,KAAK,qBAAqB,GAAG;AACrE,YAAI;AACF,gBAAM,KAAK,gBAAgB;AAC3B,sBAAY;AAAA,QACd,QAAQ;AAAA,QAER;AAAA,MACF;AACA,WAAK,iBAAiB;AACtB,WAAK,YAAY,aAAa;AAC9B,YAAM,aAAa,MACjB,KAAK;AAAA,QAAe;AAAA,QAAK,MACvB,KAAK,aAAa;AAAA,UAChB,mBAAmB;AAAA,UACnB,GAAG,mBAAmB,KAAK,UAAW;AAAA,QACxC,CAAC;AAAA,MACH;AACF,UAAIC,OAAM,MAAM,WAAW;AAC3B,WAAK,aAAaA,IAAG;AAGrB,UAAIA,KAAI,WAAW,OAAO,KAAK,eAAe,CAAC,WAAW;AACxD,YAAI,CAAC,OAAO,QAAS,QAAO,UAAU,KAAK,sBAAsB;AACjE,YAAI,OAAO,WAAW,KAAK,qBAAqB,GAAG;AACjD,gBAAM,KAAK,gBAAgB;AAC3B,UAAAA,OAAM,MAAM,WAAW;AACvB,eAAK,aAAaA,IAAG;AAAA,QACvB,WAAW,KAAK,eAAe;AAI7B,gBAAM,KAAK,cAAc,MAAM,MAAM;AAAA,UAAC,CAAC;AACvC,UAAAA,OAAM,MAAM,WAAW;AACvB,eAAK,aAAaA,IAAG;AAAA,QACvB;AAAA,MACF;AAGA,UAAIA,KAAI,WAAW,OAAO,KAAK,iBAAiB,CAAC,KAAK,aAAa;AAGjE,cAAM,mBAAmB,KAAK,mBAAmB;AACjD,aAAK,YAAY,gBAAgB;AACjC,cAAM,UAAU,KAAK,aAAa;AAAA,UAChC,mBAAmB;AAAA,UACnB,GAAG,mBAAmB,KAAK,UAAW;AAAA,QACxC,CAAC;AACD,cAAM,YAAY,MAAM,KAAK,cAAc;AAAA,UACzC;AAAA,UACA,QAAQ,KAAK;AAAA,UACb,MAAM,QAAQ;AAAA,UACd,SAAS,QAAQ;AAAA;AAAA,UAEjB,iBAAiB,KAAK,MAAM,mBAAmB,GAAS;AAAA,QAC1D,CAAC;AACD,YAAI,UAAU,WAAW,OAAO,KAAK,SAAU,QAAO,KAAK,SAAS;AACpE,YAAI,UAAU,SAAS,OAAO,UAAU,UAAU,KAAK;AACrD,gBAAM,KAAK,cAAc,UAAU,QAAQ,UAAU,MAAM,KAAK;AAAA,QAClE;AACA,aAAK,YAAY,KAAK,uBAAuB,UAAU,OAAO,KAAK,aAAa;AAChF,eAAO,KAAK,YAAY,SAAS;AAAA,MACnC;AACA,UAAIA,KAAI,WAAW,OAAO,KAAK,SAAU,QAAO,KAAK,SAAS;AAC9D,UAAI,CAACA,KAAI,GAAI,OAAM,MAAM,KAAK,QAAQA,MAAK,KAAK;AAChD,WAAK,YAAY,aAAa;AAC9B,aAAO,KAAK,aAAaA,IAAG;AAAA,IAC9B;AAIA,QAAI,OAAO,WAAW,KAAK,qBAAqB,KAAK,qBAAqB,GAAG;AAC3E,UAAI;AACJ,UAAI;AACF,2BAAmB,MAAM,mBAAmB,KAAK,cAAc,GAAG,KAAK,mBAAmB;AAAA,UACxF,cAAc,KAAK;AAAA,UACnB,iBAAiB,KAAK;AAAA;AAAA;AAAA,UAGtB,OAAO,wBAAwB,cAAc;AAAA,QAC/C,CAAC;AAAA,MACH,QAAQ;AAGN,aAAK,oBAAoB;AAAA,MAC3B;AACA,UAAI,qBAAqB,QAAW;AAClC,cAAMA,OAAM,MAAM,KAAK;AAAA,UAAe;AAAA,UAAK,MACzC,KAAK,aAAa;AAAA,YAChB,mBAAmB;AAAA,YACnB,qBAAqB;AAAA,UACvB,CAAC;AAAA,QACH;AACA,aAAK,aAAaA,IAAG;AACrB,YAAIA,KAAI,WAAW,OAAO,KAAK,SAAU,QAAO,KAAK,SAAS;AAG9D,YAAIA,KAAI,WAAW,KAAK;AACtB,cAAI,CAACA,KAAI,GAAI,OAAM,MAAM,KAAK,QAAQA,MAAK,KAAK;AAGhD,cAAI,KAAK,cAAcA,IAAG,EAAG,MAAK,YAAY,KAAK,OAAQ,MAAM;AACjE,iBAAO,KAAK,aAAaA,IAAG;AAAA,QAC9B;AAAA,MACF;AAAA,IACF;AAGA,SAAK,iBAAiB;AAKtB,QAAI,MAAM,MAAM,KAAK;AAAA,MAAe;AAAA,MAAK,YACvC,KAAK,aAAa;AAAA,QAChB,mBAAmB;AAAA,QACnB,GAAI,MAAM,KAAK,gBAAgB,KAAK,QAAQ,IAAI;AAAA,MAClD,CAAC;AAAA,IACH;AACA,SAAK,aAAa,GAAG;AAGrB,QAAI,IAAI,WAAW,KAAK;AACtB,YAAM,YAAY,IAAI,QAAQ,IAAI,kBAAkB;AACpD,UAAI,CAAC,WAAW;AACd,cAAM,MAAM,KAAK,QAAQ,KAAK,oDAAoD;AAAA,MACpF;AAGA,UAAI,CAAC,OAAO,QAAS,QAAO,UAAU,KAAK,sBAAsB;AACjE,YAAM,aAAa,OAAO,WAAW,KAAK,qBAAqB;AAC/D,YAAM,MAAM,aACR,KAAK,OAAQ,SACb,kBAAkB,WAAW,QAAW,KAAK,OAAO;AACxD,UAAI,CAAC,YAAY;AACf,aAAK,qBAAqB,GAAG;AAC7B,aAAK,YAAY,GAAG;AAAA,MACtB;AAGA,YAAM,mBAAmB,MAAM,mBAAmB,KAAK,cAAc,GAAG,WAAW;AAAA,QACjF,cAAc,aAAa,KAAK,eAAe;AAAA,QAC/C,iBAAiB,KAAK;AAAA,QACtB,OAAO,wBAAwB,cAAc;AAAA,MAC/C,CAAC;AACD,YAAM,MAAM,KAAK;AAAA,QAAe;AAAA,QAAK,MACnC,KAAK,aAAa;AAAA,UAChB,mBAAmB;AAAA,UACnB,qBAAqB;AAAA,QACvB,CAAC;AAAA,MACH;AACA,WAAK,aAAa,GAAG;AAIrB,UAAI,IAAI,OAAO,CAAC,cAAc,KAAK,cAAc,GAAG,GAAI,MAAK,YAAY,GAAG;AAAA,IAC9E;AAEA,QAAI,IAAI,WAAW,OAAO,KAAK,SAAU,QAAO,KAAK,SAAS;AAC9D,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,KAAK,QAAQ,KAAK,KAAK;AAChD,WAAO,KAAK,aAAa,GAAG;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,IAAI,KAAa,OAAgB,OAAmB,CAAC,GAAuB;AAIhF,UAAM,SAAS,EAAE,SAAS,KAAK,eAAe,EAAE;AAChD,QAAI;AACF,YAAM,YAAY,KAAK,UAAU,KAAK;AAItC,UAAI,UAAU,QAAQ,cAAc,QAAW;AAC7C,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,YAAM,KAAK,MAAM,KAAK,eAAe;AAGrC,YAAM,SAAS,QAAQ,GAAG,KAAK,GAAG;AAGlC,YAAM,aAAa,MAAM,QAAQ,GAAG,OAAO,WAAW,MAAM;AAC5D,YAAM,OAAgC;AAAA,QACpC,OAAO;AAAA,QACP,UAAU,MAAM,QAAQ,GAAG,SAAS,GAAG;AAAA,MACzC;AAEA,UAAI,KAAK,YAAY,OAAW,MAAK,WAAW,KAAK;AACrD,UAAI,KAAK,cAAc,OAAW,MAAK,aAAa,KAAK;AACzD,YAAM,UAAU,KAAK,UAAU,IAAI;AAEnC,YAAM,iBAAiB,KAAK,kBAAkB,WAAW;AACzD,YAAM,EAAE,MAAM,IAAI,IAAI,KAAK,QAAQ,MAAM;AAEzC,aAAO,MAAM,KAAK,UAAqB,QAAQ;AAAA,QAC7C,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA,eAAe;AAAA,QACf,OAAO;AAAA,QACP,cAAc,CAAC,aAAa;AAAA,UAC1B,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,oBAAoB,GAAG,QAAQ;AAAA,UAC1D,MAAM;AAAA,QACR;AAAA,QACA,cAAc,OAAO,QAAS,MAAM,IAAI,KAAK;AAAA,QAC7C,aAAa,OAAO,cAAc,KAAK,MAAM,UAAU,IAAI;AAAA,MAC7D,CAAC;AAAA,IACH,UAAE;AACA,UAAI,OAAO,QAAS,MAAK,iBAAiB;AAAA,IAC5C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,IAAiB,KAAa,OAAmB,CAAC,GAAsB;AAC5E,UAAM,EAAE,MAAM,IAAI,MAAM,KAAK,YAAe,KAAK,IAAI;AACrD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,aACJ,KACA,OAAmB,CAAC,GAC8B;AAClD,WAAO,KAAK,YAAe,KAAK,IAAI;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,YACZ,KACA,OAAmB,CAAC,GAC8B;AAIlD,UAAM,SAAS,EAAE,SAAS,KAAK,eAAe,EAAE;AAChD,QAAI;AACF,YAAM,SAAS,SAAS,MAAM,KAAK,eAAe,GAAG,KAAK,GAAG;AAC7D,YAAM,EAAE,MAAM,IAAI,IAAI,KAAK,QAAQ,MAAM;AAMzC,YAAM,iBAAiB,KAAK,kBAAkB,WAAW;AACzD,YAAM,YAAY,OAAO,QAAkE;AACzF,cAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,cAAM,gBAAgB,MAAM,KAAK,aAAa,KAAK,OAAO,GAAG;AAC7D,eAAO,EAAE,OAAO,KAAK,MAAM,aAAa,GAAQ,OAAO,KAAK,MAAM;AAAA,MACpE;AAEA,aAAO,MAAM,KAAK,UAAmD,QAAQ;AAAA,QAC3E,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA,eAAe;AAAA,QACf,OAAO;AAAA,QACP,cAAc,CAAC,aAAa,EAAE,QAAQ,OAAO,QAAQ;AAAA,QACrD,cAAc,OAAO,QAAQ,UAAU,MAAM,IAAI,KAAK,CAAC;AAAA,QACvD,aAAa,OAAO,cAAc,UAAU,UAAU,IAAI;AAAA,QAC1D,UAAU,OAAO,EAAE,OAAO,KAAK;AAAA,MACjC,CAAC;AAAA,IACH,UAAE;AACA,UAAI,OAAO,QAAS,MAAK,iBAAiB;AAAA,IAC5C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAO,KAAoC;AAC/C,UAAM,SAAS,SAAS,MAAM,KAAK,eAAe,GAAG,KAAK,GAAG;AAC7D,UAAM,EAAE,MAAM,IAAI,IAAI,KAAK,QAAQ,MAAM;AAGzC,UAAM,MAAM,MAAM,KAAK,eAAe,KAAK,aAAa;AAAA,MACtD,QAAQ;AAAA,MACR,SAAS,EAAE,GAAI,MAAM,KAAK,YAAY,UAAU,IAAI,EAAG;AAAA,IACzD,EAAE;AACF,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,MAAM,KAAK,QAAQ,KAAK,eAAe;AAAA,IAC/C;AACA,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,SACJ,OAAmD,CAAC,GACA;AACpD,UAAM,KAAK,MAAM,KAAK,eAAe;AAGrC,UAAM,SAAS,IAAI,gBAAgB;AACnC,QAAI,KAAK,OAAQ,QAAO,IAAI,UAAU,KAAK,MAAM;AACjD,QAAI,KAAK,UAAU,OAAW,QAAO,IAAI,SAAS,OAAO,KAAK,KAAK,CAAC;AACpE,UAAM,KAAK,OAAO,SAAS;AAC3B,UAAM,EAAE,MAAM,IAAI,IAAI,KAAK,MAAM;AAAA,MAC/B,MAAM;AAAA,MACN,WAAW,GAAG,EAAE;AAAA,MAChB,OAAO,MAAM;AAAA,IACf,CAAC;AAGD,UAAM,MAAM,MAAM,KAAK,eAAe,KAAK,aAAa;AAAA,MACtD,QAAQ;AAAA,MACR,SAAS,EAAE,GAAI,MAAM,KAAK,YAAY,OAAO,IAAI,EAAG;AAAA,IACtD,EAAE;AACF,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,KAAK,QAAQ,KAAK,kBAAkB;AAC7D,UAAM,OAAQ,MAAM,IAAI,KAAK;AAQ7B,UAAM,QACJ,MAAM,QAAQ;AAAA,MACZ,KAAK,MACF,OAAO,CAAC,MAA8C,EAAE,YAAY,IAAI,EACxE,IAAI,OAAO,MAAM;AAChB,YAAI;AACF,iBAAO,MAAM,QAAQ,GAAG,SAAS,EAAE,QAAQ;AAAA,QAC7C,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF,CAAC;AAAA,IACL,GACA,OAAO,CAAC,MAAmB,MAAM,IAAI;AACvC,WAAO,EAAE,MAAM,QAAQ,KAAK,OAAO;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UAA2B;AAC/B,UAAM,EAAE,MAAM,IAAI,IAAI,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG7D,UAAM,MAAM,MAAM,KAAK,eAAe,KAAK,aAAa;AAAA,MACtD,QAAQ;AAAA,MACR,SAAS,EAAE,GAAI,MAAM,KAAK,YAAY,OAAO,IAAI,EAAG;AAAA,IACtD,EAAE;AACF,SAAK,aAAa,GAAG;AACrB,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,MAAM,KAAK,QAAQ,KAAK,gBAAgB;AAAA,IAChD;AACA,UAAM,OAAQ,MAAM,IAAI,KAAK;AAG7B,QAAI,KAAK,UAAU,OAAO,SAAS,KAAK,OAAO,EAAG,MAAK,eAAe,KAAK;AAC3E,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,QACJ,WACA,OAA4D,CAAC,GACrC;AAaxB,QAAI,KAAK,cAAc,KAAK,aAAa;AACvC,YAAM,oBAAoB,YAAoC;AAC5D,aAAK,YAAY,SAAS;AAC1B,cAAM,KAAK,iBAAiB,SAAS;AACrC,cAAM,UAAU,MAAM,KAAK,QAAQ;AACnC,eAAO,EAAE,eAAe,KAAK,MAAM,YAAY,gBAAgB,GAAG,QAAQ;AAAA,MAC5E;AACA,UAAI,KAAK,gBAAgB;AACvB,eAAO,kBAAkB;AAAA,MAC3B;AACA,WAAK,iBAAiB;AACtB,UAAI;AACF,eAAO,MAAM,kBAAkB;AAAA,MACjC,UAAE;AACA,aAAK,iBAAiB;AAAA,MACxB;AAAA,IACF;AAMA,QAAI,CAAC,KAAK,UAAU,KAAK,gBAAgB;AACvC,aAAO,KAAK,WAAW,WAAW,IAAI;AAAA,IACxC;AACA,SAAK,iBAAiB;AACtB,QAAI;AACF,aAAO,MAAM,KAAK,WAAW,WAAW,IAAI;AAAA,IAC9C,UAAE;AACA,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,MAAc,iBAAiB,WAAmC;AAChE,QAAI;AACJ,QAAI;AACJ,QAAI,cAAc,QAAW;AAC3B,eAAS,KAAK,OAAQ;AACtB,wBAAkB,KAAK;AAAA,IACzB,OAAO;AACL,YAAM,SAAS,iBAAiB,SAAS;AACzC,UAAI,WAAW,QAAQ,EAAE,aAAa,IAAI;AACxC,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,eAAS;AACT,wBAAkB;AAAA,IACpB;AACA,QAAI;AACF,YAAM,KAAK,YAAa;AAAA,QACtB,YAAY,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC,EAAE;AAAA,QACrD,YAAY,KAAK;AAAA,QACjB,WAAW;AAAA,QACX;AAAA,MACF,CAAC;AAAA,IACH,SAAS,GAAG;AACV,YAAM,SAAS,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACxD,YAAM,IAAI;AAAA,QACR,2BAA2B,MAAM;AAAA,QACjC;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,SAAK,YAAY,MAAM;AAAA,EACzB;AAAA,EAEA,MAAc,WACZ,WACA,MACwB;AAMxB,QAAI,KAAK,YAAY;AACnB,YAAM,IAAI;AAAA,QACR,0JAEmB,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC,EAAE,GAAG;AAAA,QAC/D;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAKA,UAAM,eAAe,iBAAiB,SAAS;AAC/C,QAAI,iBAAiB,QAAQ,EAAE,aAAa,IAAI;AAC9C,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,SAAK,YAAY,WAAW,IAAI;AAOhC,UAAM,QAAQ,KAAK,kBAAkB,WAAW;AAChD,UAAM,EAAE,IAAI,IAAI,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEvD,QAAI,MAAM,MAAM,KAAK,eAAe,KAAK,OAAO;AAAA,MAC9C,QAAQ;AAAA,MACR,SAAS,EAAE,mBAAmB,MAAM;AAAA,IACtC,EAAE;AACF,SAAK,aAAa,GAAG;AACrB,QAAI,IAAI,WAAW,KAAK;AACtB,YAAM,YAAY,IAAI,QAAQ,IAAI,kBAAkB;AACpD,UAAI,CAAC,WAAW;AACd,cAAM,MAAM,KAAK,QAAQ,KAAK,oDAAoD;AAAA,MACpF;AACA,YAAM,mBAAmB,MAAM,mBAAmB,KAAK,cAAc,GAAG,WAAW;AAAA,QACjF;AAAA,QACA,iBAAiB,KAAK;AAAA,QACtB,eAAe,KAAK;AAAA,QACpB,OAAO,wBAAwB,KAAK;AAAA,MACtC,CAAC;AACD,YAAM,MAAM,KAAK,eAAe,KAAK,OAAO;AAAA,QAC1C,QAAQ;AAAA,QACR,SAAS,EAAE,mBAAmB,OAAO,qBAAqB,iBAAiB;AAAA,MAC7E,EAAE;AACF,WAAK,aAAa,GAAG;AAAA,IACvB;AACA,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,MAAM,KAAK,QAAQ,KAAK,gBAAgB;AAAA,IAChD;AACA,SAAK,YAAY,SAAS;AAC1B,UAAM,SAAU,MAAM,IAAI,KAAK;AAE/B,QAAI,KAAK,UAAU,OAAO,SAAS,OAAO,OAAO,EAAG,MAAK,eAAe,OAAO;AAC/E,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,MAAM,YACJ,QACA,WACA,OAA4D,CAAC,GACrC;AAGxB,QAAI,CAAC,KAAK,YAAY;AACpB,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAIA,UAAM,QAAgB,OAAO,WAAW,WAAW,oBAAoB,MAAM,IAAI;AAGjF,QAAI,CAAC,OAAO,WAAW,OAAO,MAAM,kBAAkB,YAAY;AAChE,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAMA,QAAI,CAAC,OAAO,UAAU,SAAS,KAAK,YAAY,GAAG;AACjD,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,UAAM,eAAe,YAAY;AAEjC,UAAM,EAAE,IAAI,IAAI,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACvD,UAAM,SAAS,mBAAmB,KAAK,UAAU;AAIjD,UAAM,iBAAiB,KAAK,kBAAkB,WAAW;AACzD,UAAM,QAAQ,wBAAwB,cAAc;AAGpD,QAAI,MAAM,MAAM,KAAK,eAAe,KAAK,OAAO;AAAA,MAC9C,QAAQ;AAAA,MACR,SAAS,EAAE,GAAG,QAAQ,mBAAmB,eAAe;AAAA,IAC1D,EAAE;AACF,SAAK,aAAa,GAAG;AACrB,QAAI,IAAI,WAAW,KAAK;AACtB,YAAM,YAAY,IAAI,QAAQ,IAAI,kBAAkB;AACpD,UAAI,CAAC,WAAW;AACd,cAAM,MAAM,KAAK,QAAQ,KAAK,oDAAoD;AAAA,MACpF;AAEA,YAAM,mBAAmB,MAAM,mBAAmB,OAAO,WAAW;AAAA,QAClE;AAAA,QACA,iBAAiB,KAAK;AAAA,QACtB,eAAe,KAAK;AAAA,QACpB;AAAA,MACF,CAAC;AACD,YAAM,MAAM,KAAK,eAAe,KAAK,OAAO;AAAA,QAC1C,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,GAAG;AAAA,UACH,mBAAmB;AAAA,UACnB,qBAAqB;AAAA,QACvB;AAAA,MACF,EAAE;AACF,WAAK,aAAa,GAAG;AAAA,IACvB;AACA,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,MAAM,KAAK,QAAQ,KAAK,oBAAoB;AAAA,IACpD;AACA,UAAM,SAAU,MAAM,IAAI,KAAK;AAG/B,QAAI,KAAK,UAAU,OAAO,SAAS,OAAO,OAAO,EAAG,MAAK,eAAe,OAAO;AAC/E,WAAO;AAAA,EACT;AAAA;AAAA,EAGQ,cAAc,QAAgB,UAAkB,UAAyB;AAC/E,QAAI,SAAS,UACX,OAAO;AACT,QAAI;AACF,YAAM,OAAO,KAAK,MAAM,QAAQ;AAChC,UAAI,MAAM,MAAO,UAAS,KAAK;AAC/B,UAAI,MAAM,KAAM,QAAO,KAAK;AAAA,IAC9B,QAAQ;AAAA,IAER;AACA,WAAO,IAAI,aAAa,WAAW,MAAM,KAAK,MAAM,IAAI,MAAM,MAAM;AAAA,EACtE;AAAA,EAEA,MAAc,QAAQ,KAAe,UAAkC;AACrE,WAAO,KAAK,cAAc,IAAI,QAAQ,MAAM,IAAI,KAAK,GAAG,QAAQ;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYQ,uBAAuB,SAAqD;AAClF,UAAM,MAAM,OAAO,KAAK,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,YAAY,MAAM,kBAAkB;AACnF,UAAM,SAAS,MAAM,QAAQ,GAAG,IAAI;AACpC,QAAI,CAAC,OAAQ,QAAO;AACpB,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,iBAAiB,MAAM,CAAC;AAIlD,UAAI,OAAO,OAAO,WAAW,YAAY,OAAO,WAAW,GAAI,QAAO;AACtE,YAAM,SAAS,OAAO,OAAO,MAAM;AACnC,aAAO,OAAO,SAAS,MAAM,KAAK,SAAS,IAAI,SAAS,MAAY;AAAA,IACtE,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;","names":["hexToBytes","hexToBytes","res"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentkv/client",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AgentKV client SDK: zero-knowledge encryption + x402/EIP-712 signing for the agent-native, pay-per-request KV store",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "The AgentKV Authors <contact@agentx402.ai>",
|
|
7
|
+
"homepage": "https://github.com/agentx402-ai/agentkv#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/agentx402-ai/agentkv.git",
|
|
11
|
+
"directory": "client"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/agentx402-ai/agentkv/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"agentkv",
|
|
18
|
+
"x402",
|
|
19
|
+
"key-value",
|
|
20
|
+
"kv-store",
|
|
21
|
+
"encrypted",
|
|
22
|
+
"zero-knowledge",
|
|
23
|
+
"usdc",
|
|
24
|
+
"agent",
|
|
25
|
+
"sdk",
|
|
26
|
+
"eip-3009",
|
|
27
|
+
"eip-712",
|
|
28
|
+
"viem"
|
|
29
|
+
],
|
|
30
|
+
"type": "module",
|
|
31
|
+
"sideEffects": false,
|
|
32
|
+
"main": "./dist/index.cjs",
|
|
33
|
+
"module": "./dist/index.js",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"import": {
|
|
38
|
+
"types": "./dist/index.d.ts",
|
|
39
|
+
"default": "./dist/index.js"
|
|
40
|
+
},
|
|
41
|
+
"require": {
|
|
42
|
+
"types": "./dist/index.d.cts",
|
|
43
|
+
"default": "./dist/index.cjs"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"dist"
|
|
49
|
+
],
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsup",
|
|
55
|
+
"test": "tsc --noEmit && vitest run",
|
|
56
|
+
"test:watch": "vitest",
|
|
57
|
+
"test:coverage": "vitest run --coverage",
|
|
58
|
+
"typecheck": "tsc --noEmit"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@agentx402-ai/core": "^0.1.0",
|
|
62
|
+
"@noble/hashes": "^1.4.0",
|
|
63
|
+
"viem": "^2.21.0"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@types/node": "^20.0.0",
|
|
67
|
+
"@x402/core": "^2.16.0",
|
|
68
|
+
"@x402/evm": "^2.16.0",
|
|
69
|
+
"tsup": "^8.3.0",
|
|
70
|
+
"typescript": "^5.6.0",
|
|
71
|
+
"vitest": "^4.1.0"
|
|
72
|
+
},
|
|
73
|
+
"engines": {
|
|
74
|
+
"node": ">=20"
|
|
75
|
+
}
|
|
76
|
+
}
|