kontext-sdk 0.9.0 → 0.11.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/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var crypto$1 = require('crypto');
4
- var fs4 = require('fs');
5
- var path4 = require('path');
4
+ var fs5 = require('fs');
5
+ var path5 = require('path');
6
6
 
7
7
  function _interopNamespace(e) {
8
8
  if (e && e.__esModule) return e;
@@ -22,8 +22,8 @@ function _interopNamespace(e) {
22
22
  return Object.freeze(n);
23
23
  }
24
24
 
25
- var fs4__namespace = /*#__PURE__*/_interopNamespace(fs4);
26
- var path4__namespace = /*#__PURE__*/_interopNamespace(path4);
25
+ var fs5__namespace = /*#__PURE__*/_interopNamespace(fs5);
26
+ var path5__namespace = /*#__PURE__*/_interopNamespace(path5);
27
27
 
28
28
  var __defProp = Object.defineProperty;
29
29
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -1974,13 +1974,13 @@ var ActionLogger = class {
1974
1974
  }
1975
1975
  flushToFile(actions) {
1976
1976
  const outputDir = this.config.localOutputDir ?? ".kontext";
1977
- const logDir = path4__namespace.join(outputDir, "logs");
1977
+ const logDir = path5__namespace.join(outputDir, "logs");
1978
1978
  try {
1979
- fs4__namespace.mkdirSync(logDir, { recursive: true });
1979
+ fs5__namespace.mkdirSync(logDir, { recursive: true });
1980
1980
  const filename = `actions-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.jsonl`;
1981
- const filePath = path4__namespace.join(logDir, filename);
1981
+ const filePath = path5__namespace.join(logDir, filename);
1982
1982
  const lines = actions.map((a) => JSON.stringify(a)).join("\n") + "\n";
1983
- fs4__namespace.appendFileSync(filePath, lines, "utf-8");
1983
+ fs5__namespace.appendFileSync(filePath, lines, "utf-8");
1984
1984
  } catch (error) {
1985
1985
  this.emitLog("warn", "Failed to write log file", { error });
1986
1986
  }
@@ -2590,9 +2590,9 @@ var SANCTIONED_SET = new Set(
2590
2590
  function loadCachedSDN() {
2591
2591
  try {
2592
2592
  const dataDir = process.env["KONTEXT_DATA_DIR"] || ".kontext";
2593
- const cachePath = path4__namespace.join(dataDir, "ofac-sdn-cache.json");
2594
- if (fs4__namespace.existsSync(cachePath)) {
2595
- const cache = JSON.parse(fs4__namespace.readFileSync(cachePath, "utf-8"));
2593
+ const cachePath = path5__namespace.join(dataDir, "ofac-sdn-cache.json");
2594
+ if (fs5__namespace.existsSync(cachePath)) {
2595
+ const cache = JSON.parse(fs5__namespace.readFileSync(cachePath, "utf-8"));
2596
2596
  if (Array.isArray(cache.addresses)) {
2597
2597
  for (const addr of cache.addresses) {
2598
2598
  SANCTIONED_SET.add(String(addr).toLowerCase());
@@ -3648,7 +3648,9 @@ var FEATURE_MIN_PLAN = {
3648
3648
  "unified-screening": "pro",
3649
3649
  "blocklist-manager": "pro",
3650
3650
  "kya-identity": "pro",
3651
- "kya-behavioral": "enterprise"
3651
+ "kya-behavioral": "enterprise",
3652
+ "coinbase-wallets": "enterprise",
3653
+ "metamask-wallets": "enterprise"
3652
3654
  };
3653
3655
  var PLAN_RANK = { free: 0, pro: 1, enterprise: 2 };
3654
3656
  var FEATURE_LABELS = {
@@ -3667,7 +3669,9 @@ var FEATURE_LABELS = {
3667
3669
  "unified-screening": "Unified screening (OFAC, Chainalysis, OpenSanctions)",
3668
3670
  "blocklist-manager": "Custom blocklist/allowlist manager",
3669
3671
  "kya-identity": "KYA identity resolution (declared identity, wallet clustering)",
3670
- "kya-behavioral": "KYA behavioral fingerprinting (cross-session linking, confidence scoring)"
3672
+ "kya-behavioral": "KYA behavioral fingerprinting (cross-session linking, confidence scoring)",
3673
+ "coinbase-wallets": "Coinbase Developer Platform Wallets",
3674
+ "metamask-wallets": "MetaMask Embedded Wallets"
3671
3675
  };
3672
3676
  function isFeatureAvailable(feature, currentPlan) {
3673
3677
  const requiredPlan = FEATURE_MIN_PLAN[feature];
@@ -3713,7 +3717,7 @@ var JsonFileExporter = class {
3713
3717
  buffer = [];
3714
3718
  bufferSize;
3715
3719
  constructor(options) {
3716
- this.outputDir = path4__namespace.resolve(options?.outputDir ?? ".kontext/exports");
3720
+ this.outputDir = path5__namespace.resolve(options?.outputDir ?? ".kontext/exports");
3717
3721
  this.bufferSize = options?.bufferSize ?? 1;
3718
3722
  }
3719
3723
  async export(events) {
@@ -3728,11 +3732,11 @@ var JsonFileExporter = class {
3728
3732
  const toWrite = [...this.buffer];
3729
3733
  this.buffer = [];
3730
3734
  try {
3731
- fs4__namespace.mkdirSync(this.outputDir, { recursive: true });
3735
+ fs5__namespace.mkdirSync(this.outputDir, { recursive: true });
3732
3736
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
3733
- const filePath = path4__namespace.join(this.outputDir, `events-${date}.jsonl`);
3737
+ const filePath = path5__namespace.join(this.outputDir, `events-${date}.jsonl`);
3734
3738
  const lines = toWrite.map((e) => JSON.stringify(e)).join("\n") + "\n";
3735
- fs4__namespace.appendFileSync(filePath, lines, "utf-8");
3739
+ fs5__namespace.appendFileSync(filePath, lines, "utf-8");
3736
3740
  } catch (error) {
3737
3741
  console.warn("[Kontext JsonFileExporter] Failed to write events:", error);
3738
3742
  }
@@ -3902,6 +3906,218 @@ function extractDocumentId(resourceName) {
3902
3906
  const parts = resourceName.split("/");
3903
3907
  return parts[parts.length - 1] ?? resourceName;
3904
3908
  }
3909
+ var CONFIG_FILENAME = "kontext.config.json";
3910
+ function loadConfigFile(startDir) {
3911
+ const dir = startDir ?? process.cwd();
3912
+ const filePath = findConfigFile(dir);
3913
+ if (!filePath) return null;
3914
+ try {
3915
+ const raw = fs5__namespace.readFileSync(filePath, "utf-8");
3916
+ const parsed = JSON.parse(raw);
3917
+ if (!parsed.projectId || typeof parsed.projectId !== "string") {
3918
+ return null;
3919
+ }
3920
+ return parsed;
3921
+ } catch {
3922
+ return null;
3923
+ }
3924
+ }
3925
+ function findConfigFile(dir) {
3926
+ let current = path5__namespace.resolve(dir);
3927
+ const root = path5__namespace.parse(current).root;
3928
+ while (true) {
3929
+ const candidate = path5__namespace.join(current, CONFIG_FILENAME);
3930
+ if (fs5__namespace.existsSync(candidate)) {
3931
+ return candidate;
3932
+ }
3933
+ const parent = path5__namespace.dirname(current);
3934
+ if (parent === current || current === root) {
3935
+ return null;
3936
+ }
3937
+ current = parent;
3938
+ }
3939
+ }
3940
+
3941
+ // src/integrations/data/stablecoin-contracts.ts
3942
+ var STABLECOIN_CONTRACTS = {
3943
+ // USDC (6 decimals)
3944
+ "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": { token: "USDC", chain: "ethereum", decimals: 6 },
3945
+ "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913": { token: "USDC", chain: "base", decimals: 6 },
3946
+ "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359": { token: "USDC", chain: "polygon", decimals: 6 },
3947
+ "0xaf88d065e77c8cc2239327c5edb3a432268e5831": { token: "USDC", chain: "arbitrum", decimals: 6 },
3948
+ "0x0b2c639c533813f4aa9d7837caf62653d097ff85": { token: "USDC", chain: "optimism", decimals: 6 },
3949
+ "0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e": { token: "USDC", chain: "avalanche", decimals: 6 },
3950
+ // USDT (6 decimals)
3951
+ "0xdac17f958d2ee523a2206206994597c13d831ec7": { token: "USDT", chain: "ethereum", decimals: 6 },
3952
+ "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9": { token: "USDT", chain: "arbitrum", decimals: 6 },
3953
+ "0x94b008aa00579c1307b0ef2c499ad98a8ce58e58": { token: "USDT", chain: "optimism", decimals: 6 },
3954
+ "0xc2132d05d31c914a87c6611c10748aeb04b58e8f": { token: "USDT", chain: "polygon", decimals: 6 },
3955
+ // DAI (18 decimals)
3956
+ "0x6b175474e89094c44da98b954eedeac495271d0f": { token: "DAI", chain: "ethereum", decimals: 18 },
3957
+ "0x50c5725949a6f0c72e6c4a641f24049a917db0cb": { token: "DAI", chain: "base", decimals: 18 },
3958
+ // EURC (6 decimals)
3959
+ "0x1abaea1f7c830bd89acc67ec4af516284b1bc33c": { token: "EURC", chain: "ethereum", decimals: 6 },
3960
+ "0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42": { token: "EURC", chain: "base", decimals: 6 }
3961
+ };
3962
+ new Set(Object.keys(STABLECOIN_CONTRACTS));
3963
+ var CHAIN_ID_MAP = {
3964
+ 1: "ethereum",
3965
+ 8453: "base",
3966
+ 137: "polygon",
3967
+ 42161: "arbitrum",
3968
+ 10: "optimism",
3969
+ 43114: "avalanche"
3970
+ };
3971
+ var TRANSFER_SELECTOR = "0xa9059cbb";
3972
+ var TRANSFER_FROM_SELECTOR = "0x23b872dd";
3973
+ var TRANSFER_EVENT_ABI = {
3974
+ type: "event",
3975
+ name: "Transfer",
3976
+ inputs: [
3977
+ { type: "address", name: "from", indexed: true },
3978
+ { type: "address", name: "to", indexed: true },
3979
+ { type: "uint256", name: "value", indexed: false }
3980
+ ]
3981
+ };
3982
+
3983
+ // src/integrations/wallet-monitor.ts
3984
+ var WalletMonitor = class {
3985
+ kontext;
3986
+ config;
3987
+ agentId;
3988
+ tokens;
3989
+ unwatchers = [];
3990
+ running = false;
3991
+ /** Shared dedup set — tracks recently verified txHashes (populated by both layers) */
3992
+ verifiedTxHashes = /* @__PURE__ */ new Set();
3993
+ cleanupTimer = null;
3994
+ txTimestamps = /* @__PURE__ */ new Map();
3995
+ constructor(kontext, config, options) {
3996
+ this.kontext = kontext;
3997
+ this.config = config;
3998
+ this.agentId = options?.agentId ?? "wallet-monitor";
3999
+ this.tokens = options?.tokens ? new Set(options.tokens) : null;
4000
+ }
4001
+ /**
4002
+ * Mark a txHash as already verified (called by the viem interceptor layer).
4003
+ * The monitor will skip this tx if it later sees it on-chain.
4004
+ */
4005
+ markVerified(txHash) {
4006
+ const lower = txHash.toLowerCase();
4007
+ this.verifiedTxHashes.add(lower);
4008
+ this.txTimestamps.set(lower, Date.now());
4009
+ }
4010
+ /**
4011
+ * Start watching all configured chains for stablecoin transfers.
4012
+ * Dynamically imports viem — requires viem as a peer dependency.
4013
+ */
4014
+ async start() {
4015
+ if (this.running) return;
4016
+ let viem;
4017
+ try {
4018
+ viem = await import('viem');
4019
+ } catch {
4020
+ throw new Error(
4021
+ "Wallet monitoring requires viem. Install it: npm install viem"
4022
+ );
4023
+ }
4024
+ const { createPublicClient, http } = viem;
4025
+ const wallets = this.config.wallets.map((w) => w.toLowerCase());
4026
+ const pollingInterval = this.config.pollingIntervalMs ?? 12e3;
4027
+ const contractsByChain = /* @__PURE__ */ new Map();
4028
+ for (const [address, info] of Object.entries(STABLECOIN_CONTRACTS)) {
4029
+ if (this.tokens && !this.tokens.has(info.token)) continue;
4030
+ const existing = contractsByChain.get(info.chain) ?? [];
4031
+ existing.push({ address, info });
4032
+ contractsByChain.set(info.chain, existing);
4033
+ }
4034
+ for (const [chain, contracts] of contractsByChain) {
4035
+ const rpcUrl = this.config.rpcEndpoints[chain];
4036
+ if (!rpcUrl) continue;
4037
+ const client = createPublicClient({
4038
+ transport: http(rpcUrl)
4039
+ });
4040
+ for (const { address, info } of contracts) {
4041
+ const unwatch = client.watchEvent({
4042
+ address,
4043
+ event: TRANSFER_EVENT_ABI,
4044
+ args: { from: wallets.length === 1 ? wallets[0] : wallets },
4045
+ poll: true,
4046
+ pollingInterval,
4047
+ onLogs: (logs) => {
4048
+ for (const log of logs) {
4049
+ this.handleTransferLog(log, info);
4050
+ }
4051
+ }
4052
+ });
4053
+ this.unwatchers.push(unwatch);
4054
+ }
4055
+ }
4056
+ this.cleanupTimer = setInterval(() => {
4057
+ const cutoff = Date.now() - 5 * 60 * 1e3;
4058
+ for (const [hash, ts] of this.txTimestamps) {
4059
+ if (ts < cutoff) {
4060
+ this.verifiedTxHashes.delete(hash);
4061
+ this.txTimestamps.delete(hash);
4062
+ }
4063
+ }
4064
+ }, 6e4);
4065
+ this.running = true;
4066
+ }
4067
+ /** Stop all watchers and cleanup */
4068
+ stop() {
4069
+ for (const unwatch of this.unwatchers) {
4070
+ try {
4071
+ unwatch();
4072
+ } catch {
4073
+ }
4074
+ }
4075
+ this.unwatchers.length = 0;
4076
+ if (this.cleanupTimer) {
4077
+ clearInterval(this.cleanupTimer);
4078
+ this.cleanupTimer = null;
4079
+ }
4080
+ this.running = false;
4081
+ }
4082
+ isRunning() {
4083
+ return this.running;
4084
+ }
4085
+ handleTransferLog(log, contractInfo) {
4086
+ const txHash = log.transactionHash;
4087
+ if (!txHash) return;
4088
+ const lowerHash = txHash.toLowerCase();
4089
+ if (this.verifiedTxHashes.has(lowerHash)) return;
4090
+ this.markVerified(txHash);
4091
+ const from = log.args?.from?.toLowerCase() ?? "";
4092
+ const to = log.args?.to?.toLowerCase() ?? "";
4093
+ const value = log.args?.value;
4094
+ if (!from || !to || value === void 0) return;
4095
+ const amount = formatTokenAmount(value, contractInfo.decimals);
4096
+ const verifyInput = {
4097
+ txHash,
4098
+ chain: contractInfo.chain,
4099
+ amount,
4100
+ token: contractInfo.token,
4101
+ from,
4102
+ to,
4103
+ agentId: this.agentId,
4104
+ metadata: {
4105
+ source: "wallet-monitor",
4106
+ contractAddress: log.address
4107
+ }
4108
+ };
4109
+ this.kontext.verify(verifyInput).catch(() => {
4110
+ });
4111
+ }
4112
+ };
4113
+ function formatTokenAmount(amount, decimals) {
4114
+ const divisor = BigInt(10 ** decimals);
4115
+ const whole = amount / divisor;
4116
+ const fraction = amount % divisor;
4117
+ if (fraction === 0n) return whole.toString();
4118
+ const fractionStr = fraction.toString().padStart(decimals, "0").replace(/0+$/, "");
4119
+ return `${whole}.${fractionStr}`;
4120
+ }
3905
4121
  var ProvenanceManager = class {
3906
4122
  store;
3907
4123
  logger;
@@ -5421,6 +5637,602 @@ var KYAConfidenceScorer = class {
5421
5637
  }
5422
5638
  };
5423
5639
 
5640
+ // src/integrations/circle-wallets.ts
5641
+ var DEFAULT_BASE_URL = "https://api.circle.com";
5642
+ var REQUEST_TIMEOUT_MS = 1e4;
5643
+ var CircleWalletManager = class {
5644
+ apiKey;
5645
+ entitySecret;
5646
+ baseUrl;
5647
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5648
+ kontext = null;
5649
+ constructor(config) {
5650
+ if (!config.apiKey) {
5651
+ throw new KontextError(
5652
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
5653
+ "Circle API key is required"
5654
+ );
5655
+ }
5656
+ if (!config.entitySecret) {
5657
+ throw new KontextError(
5658
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
5659
+ "Circle entity secret is required"
5660
+ );
5661
+ }
5662
+ this.apiKey = config.apiKey;
5663
+ this.entitySecret = config.entitySecret;
5664
+ this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
5665
+ }
5666
+ /** Link to Kontext instance for auto-compliance logging */
5667
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5668
+ setKontext(kontext) {
5669
+ this.kontext = kontext;
5670
+ }
5671
+ /** Validate credentials by calling Circle's configuration endpoint */
5672
+ async validateCredentials() {
5673
+ try {
5674
+ const res = await fetch(`${this.baseUrl}/v1/w3s/config/entity`, {
5675
+ method: "GET",
5676
+ headers: this.headers(),
5677
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
5678
+ });
5679
+ return res.ok;
5680
+ } catch {
5681
+ return false;
5682
+ }
5683
+ }
5684
+ /** Create a wallet set (container for wallets) */
5685
+ async createWalletSet(input) {
5686
+ const body = {
5687
+ name: input.name,
5688
+ idempotencyKey: input.idempotencyKey ?? generateId(),
5689
+ entitySecretCiphertext: this.entitySecret
5690
+ };
5691
+ const res = await this.request(
5692
+ "POST",
5693
+ "/v1/w3s/developer/walletSets",
5694
+ body
5695
+ );
5696
+ return res.data.walletSet;
5697
+ }
5698
+ /** Create wallet(s) in a wallet set */
5699
+ async createWallet(input) {
5700
+ const body = {
5701
+ walletSetId: input.walletSetId,
5702
+ blockchains: input.blockchains.map((c) => this.mapChain(c)),
5703
+ count: input.count ?? 1,
5704
+ accountType: input.accountType ?? "EOA",
5705
+ idempotencyKey: input.idempotencyKey ?? generateId(),
5706
+ entitySecretCiphertext: this.entitySecret
5707
+ };
5708
+ const res = await this.request(
5709
+ "POST",
5710
+ "/v1/w3s/developer/wallets",
5711
+ body
5712
+ );
5713
+ return res.data.wallets;
5714
+ }
5715
+ /** List wallets, optionally filtered by wallet set */
5716
+ async listWallets(walletSetId) {
5717
+ const qs = walletSetId ? `?walletSetId=${walletSetId}` : "";
5718
+ const res = await this.request(
5719
+ "GET",
5720
+ `/v1/w3s/wallets${qs}`
5721
+ );
5722
+ return res.data.wallets;
5723
+ }
5724
+ /** Get wallet token balances */
5725
+ async getBalance(walletId) {
5726
+ const res = await this.request(
5727
+ "GET",
5728
+ `/v1/w3s/wallets/${walletId}/balances`
5729
+ );
5730
+ return res.data.tokenBalances.map((b) => ({
5731
+ token: b.token.symbol,
5732
+ amount: b.amount
5733
+ }));
5734
+ }
5735
+ /** Transfer with auto-compliance: runs verify() before/after transfer */
5736
+ async transferWithCompliance(input) {
5737
+ let complianceResult;
5738
+ if (this.kontext) {
5739
+ complianceResult = await this.kontext.verify({
5740
+ txHash: `circle-pending-${generateId()}`,
5741
+ chain: input.blockchain,
5742
+ amount: input.amount,
5743
+ token: "USDC",
5744
+ from: input.walletId,
5745
+ to: input.destinationAddress,
5746
+ agentId: input.agentId ?? "circle-wallet-manager"
5747
+ });
5748
+ if (!complianceResult.compliant) {
5749
+ return {
5750
+ id: "",
5751
+ state: "BLOCKED",
5752
+ complianceResult
5753
+ };
5754
+ }
5755
+ }
5756
+ const body = {
5757
+ walletId: input.walletId,
5758
+ tokenAddress: input.tokenAddress,
5759
+ destinationAddress: input.destinationAddress,
5760
+ amounts: [input.amount],
5761
+ blockchain: this.mapChain(input.blockchain),
5762
+ idempotencyKey: input.idempotencyKey ?? generateId(),
5763
+ entitySecretCiphertext: this.entitySecret,
5764
+ feeLevel: "MEDIUM"
5765
+ };
5766
+ const res = await this.request(
5767
+ "POST",
5768
+ "/v1/w3s/developer/transactions/transfer",
5769
+ body
5770
+ );
5771
+ if (this.kontext) {
5772
+ await this.kontext.logReasoning({
5773
+ agentId: input.agentId ?? "circle-wallet-manager",
5774
+ action: "circle-transfer",
5775
+ reasoning: `Circle transfer ${res.data.id}: ${input.amount} to ${input.destinationAddress} on ${input.blockchain}`,
5776
+ confidence: 1,
5777
+ context: { transferId: res.data.id, state: res.data.state }
5778
+ });
5779
+ }
5780
+ return {
5781
+ id: res.data.id,
5782
+ state: res.data.state,
5783
+ txHash: res.data.txHash,
5784
+ complianceResult
5785
+ };
5786
+ }
5787
+ // --------------------------------------------------------------------------
5788
+ // Private helpers
5789
+ // --------------------------------------------------------------------------
5790
+ headers() {
5791
+ return {
5792
+ "Authorization": `Bearer ${this.apiKey}`,
5793
+ "Content-Type": "application/json",
5794
+ "X-Entity-Secret": this.entitySecret
5795
+ };
5796
+ }
5797
+ async request(method, path6, body) {
5798
+ const res = await fetch(`${this.baseUrl}${path6}`, {
5799
+ method,
5800
+ headers: this.headers(),
5801
+ body: body ? JSON.stringify(body) : void 0,
5802
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
5803
+ });
5804
+ if (!res.ok) {
5805
+ const text = await res.text().catch(() => "");
5806
+ throw new KontextError(
5807
+ "API_ERROR" /* API_ERROR */,
5808
+ `Circle API error ${res.status}: ${text}`
5809
+ );
5810
+ }
5811
+ return res.json();
5812
+ }
5813
+ mapChain(chain) {
5814
+ const map = {
5815
+ ethereum: "ETH",
5816
+ base: "BASE",
5817
+ polygon: "MATIC",
5818
+ arbitrum: "ARB",
5819
+ optimism: "OP",
5820
+ avalanche: "AVAX",
5821
+ solana: "SOL"
5822
+ };
5823
+ return map[chain] ?? chain.toUpperCase();
5824
+ }
5825
+ };
5826
+
5827
+ // src/integrations/coinbase-wallets.ts
5828
+ var CDP_BASE_URL = "https://api.cdp.coinbase.com";
5829
+ var REQUEST_TIMEOUT_MS2 = 1e4;
5830
+ var CoinbaseWalletManager = class {
5831
+ apiKeyId;
5832
+ apiKeySecret;
5833
+ walletSecret;
5834
+ baseUrl;
5835
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5836
+ kontext = null;
5837
+ constructor(config) {
5838
+ if (!config.apiKeyId) {
5839
+ throw new KontextError(
5840
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
5841
+ "Coinbase CDP API Key ID is required"
5842
+ );
5843
+ }
5844
+ if (!config.apiKeySecret) {
5845
+ throw new KontextError(
5846
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
5847
+ "Coinbase CDP API Key Secret is required"
5848
+ );
5849
+ }
5850
+ if (!config.walletSecret) {
5851
+ throw new KontextError(
5852
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
5853
+ "Coinbase CDP Wallet Secret is required"
5854
+ );
5855
+ }
5856
+ this.apiKeyId = config.apiKeyId;
5857
+ this.apiKeySecret = config.apiKeySecret;
5858
+ this.walletSecret = config.walletSecret;
5859
+ this.baseUrl = CDP_BASE_URL;
5860
+ }
5861
+ /** Link to Kontext instance for auto-compliance logging */
5862
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5863
+ setKontext(kontext) {
5864
+ this.kontext = kontext;
5865
+ }
5866
+ /** Validate credentials by listing accounts */
5867
+ async validateCredentials() {
5868
+ try {
5869
+ const res = await fetch(`${this.baseUrl}/v1/evm/accounts`, {
5870
+ method: "GET",
5871
+ headers: await this.headers(),
5872
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS2)
5873
+ });
5874
+ return res.ok;
5875
+ } catch {
5876
+ return false;
5877
+ }
5878
+ }
5879
+ /** Create an EVM account */
5880
+ async createAccount(opts) {
5881
+ const body = {};
5882
+ if (opts?.name) body["name"] = opts.name;
5883
+ if (opts?.network) body["network"] = opts.network;
5884
+ const res = await this.request(
5885
+ "POST",
5886
+ "/v1/evm/accounts",
5887
+ body,
5888
+ true
5889
+ // requires wallet auth
5890
+ );
5891
+ return {
5892
+ address: res.address,
5893
+ name: res.name,
5894
+ network: res.network
5895
+ };
5896
+ }
5897
+ /** List accounts */
5898
+ async listAccounts() {
5899
+ const res = await this.request(
5900
+ "GET",
5901
+ "/v1/evm/accounts"
5902
+ );
5903
+ return res.accounts.map((a) => ({
5904
+ address: a.address,
5905
+ name: a.name,
5906
+ network: a.network
5907
+ }));
5908
+ }
5909
+ /** Get token balances for an address */
5910
+ async getBalances(address, network) {
5911
+ const res = await this.request(
5912
+ "GET",
5913
+ `/v1/evm/accounts/${address}/balances?network=${network}`
5914
+ );
5915
+ return res.balances.map((b) => ({
5916
+ token: b.asset,
5917
+ amount: b.amount
5918
+ }));
5919
+ }
5920
+ /** Transfer with auto-compliance: runs verify() before/after transfer */
5921
+ async transferWithCompliance(input) {
5922
+ let complianceResult;
5923
+ if (this.kontext) {
5924
+ const chain = this.mapNetwork(input.network);
5925
+ complianceResult = await this.kontext.verify({
5926
+ txHash: `cdp-pending-${generateId()}`,
5927
+ chain,
5928
+ amount: input.amount,
5929
+ token: input.token,
5930
+ from: input.fromAddress,
5931
+ to: input.toAddress,
5932
+ agentId: input.agentId ?? "coinbase-wallet-manager"
5933
+ });
5934
+ if (!complianceResult.compliant) {
5935
+ return {
5936
+ transactionHash: "",
5937
+ status: "BLOCKED",
5938
+ complianceResult
5939
+ };
5940
+ }
5941
+ }
5942
+ const body = {
5943
+ to: input.toAddress,
5944
+ amount: input.amount,
5945
+ asset: input.token,
5946
+ network: input.network
5947
+ };
5948
+ const res = await this.request(
5949
+ "POST",
5950
+ `/v1/evm/accounts/${input.fromAddress}/transfer`,
5951
+ body,
5952
+ true
5953
+ // requires wallet auth
5954
+ );
5955
+ if (this.kontext) {
5956
+ await this.kontext.logReasoning({
5957
+ agentId: input.agentId ?? "coinbase-wallet-manager",
5958
+ action: "coinbase-transfer",
5959
+ reasoning: `CDP transfer: ${input.amount} ${input.token} from ${input.fromAddress} to ${input.toAddress} on ${input.network}`,
5960
+ confidence: 1,
5961
+ context: { transactionHash: res.transactionHash, status: res.status }
5962
+ });
5963
+ }
5964
+ return {
5965
+ transactionHash: res.transactionHash,
5966
+ status: res.status,
5967
+ complianceResult
5968
+ };
5969
+ }
5970
+ // --------------------------------------------------------------------------
5971
+ // Private helpers
5972
+ // --------------------------------------------------------------------------
5973
+ /**
5974
+ * Build auth headers. CDP uses JWT Bearer tokens:
5975
+ * - API auth: signed with apiKeySecret, apiKeyId as kid, 120s expiry
5976
+ * - Wallet auth: X-Wallet-Auth header signed with walletSecret, 60s expiry
5977
+ *
5978
+ * Note: Full Ed25519 JWT signing requires the jose or crypto module.
5979
+ * This implementation provides the header structure; production use
5980
+ * should integrate with @coinbase/cdp-sdk for proper JWT signing.
5981
+ */
5982
+ async headers(includeWalletAuth = false) {
5983
+ const apiJwt = this.buildJwt(this.apiKeyId, this.apiKeySecret, 120);
5984
+ const hdrs = {
5985
+ "Authorization": `Bearer ${apiJwt}`,
5986
+ "Content-Type": "application/json"
5987
+ };
5988
+ if (includeWalletAuth) {
5989
+ const walletJwt = this.buildJwt("wallet", this.walletSecret, 60);
5990
+ hdrs["X-Wallet-Auth"] = walletJwt;
5991
+ }
5992
+ return hdrs;
5993
+ }
5994
+ /**
5995
+ * Build a minimal JWT structure. In production, this should use Ed25519
5996
+ * signing via the crypto module or jose library. Here we build the
5997
+ * structure that CDP expects.
5998
+ */
5999
+ buildJwt(kid, _secret, expirySeconds) {
6000
+ const header = { alg: "EdDSA", typ: "JWT", kid };
6001
+ const nowSec = Math.floor(Date.now() / 1e3);
6002
+ const payload = {
6003
+ iss: this.apiKeyId,
6004
+ sub: this.apiKeyId,
6005
+ aud: ["cdp"],
6006
+ iat: nowSec,
6007
+ exp: nowSec + expirySeconds,
6008
+ jti: generateId()
6009
+ };
6010
+ const b64Header = Buffer.from(JSON.stringify(header)).toString("base64url");
6011
+ const b64Payload = Buffer.from(JSON.stringify(payload)).toString("base64url");
6012
+ return `${b64Header}.${b64Payload}.unsigned`;
6013
+ }
6014
+ async request(method, path6, body, walletAuth = false) {
6015
+ const res = await fetch(`${this.baseUrl}${path6}`, {
6016
+ method,
6017
+ headers: await this.headers(walletAuth),
6018
+ body: body ? JSON.stringify(body) : void 0,
6019
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS2)
6020
+ });
6021
+ if (!res.ok) {
6022
+ const text = await res.text().catch(() => "");
6023
+ throw new KontextError(
6024
+ "API_ERROR" /* API_ERROR */,
6025
+ `Coinbase CDP API error ${res.status}: ${text}`
6026
+ );
6027
+ }
6028
+ return res.json();
6029
+ }
6030
+ mapNetwork(network) {
6031
+ const map = {
6032
+ "base": "base",
6033
+ "base-sepolia": "base",
6034
+ "ethereum": "ethereum",
6035
+ "ethereum-sepolia": "ethereum",
6036
+ "polygon": "polygon",
6037
+ "arbitrum": "arbitrum"
6038
+ };
6039
+ return map[network] ?? network;
6040
+ }
6041
+ };
6042
+
6043
+ // src/integrations/metamask-wallets.ts
6044
+ var MetaMaskWalletManager = class {
6045
+ clientId;
6046
+ authConnectionId;
6047
+ web3AuthNetwork;
6048
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6049
+ kontext = null;
6050
+ constructor(config) {
6051
+ if (!config.clientId) {
6052
+ throw new KontextError(
6053
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
6054
+ "MetaMask Web3Auth Client ID is required"
6055
+ );
6056
+ }
6057
+ if (!config.authConnectionId) {
6058
+ throw new KontextError(
6059
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
6060
+ "MetaMask Auth Connection ID is required"
6061
+ );
6062
+ }
6063
+ this.clientId = config.clientId;
6064
+ this.authConnectionId = config.authConnectionId;
6065
+ this.web3AuthNetwork = config.web3AuthNetwork ?? "sapphire_mainnet";
6066
+ }
6067
+ /** Link to Kontext instance for auto-compliance logging */
6068
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6069
+ setKontext(kontext) {
6070
+ this.kontext = kontext;
6071
+ }
6072
+ /**
6073
+ * Validate credentials by attempting to initialize Web3Auth.
6074
+ * Returns false if @web3auth/node-sdk is not installed.
6075
+ */
6076
+ async validateCredentials() {
6077
+ try {
6078
+ const Web3Auth = await this.loadWeb3Auth();
6079
+ if (!Web3Auth) return false;
6080
+ const w3a = new Web3Auth({
6081
+ clientId: this.clientId,
6082
+ web3AuthNetwork: this.web3AuthNetwork
6083
+ });
6084
+ await w3a.init();
6085
+ return true;
6086
+ } catch {
6087
+ return false;
6088
+ }
6089
+ }
6090
+ /**
6091
+ * Connect and get account for a user.
6092
+ * Requires a JWT idToken for custom auth via authConnectionId.
6093
+ */
6094
+ async connect(idToken) {
6095
+ const Web3Auth = await this.requireWeb3Auth();
6096
+ const w3a = new Web3Auth({
6097
+ clientId: this.clientId,
6098
+ web3AuthNetwork: this.web3AuthNetwork
6099
+ });
6100
+ await w3a.init();
6101
+ const provider = await w3a.connect({
6102
+ verifier: this.authConnectionId,
6103
+ verifierId: "user",
6104
+ idToken
6105
+ });
6106
+ if (!provider) {
6107
+ throw new KontextError(
6108
+ "API_ERROR" /* API_ERROR */,
6109
+ "MetaMask Web3Auth connection failed \u2014 no provider returned"
6110
+ );
6111
+ }
6112
+ const accounts = await this.getAccounts(provider);
6113
+ if (accounts.length === 0) {
6114
+ throw new KontextError(
6115
+ "API_ERROR" /* API_ERROR */,
6116
+ "MetaMask connection returned no accounts"
6117
+ );
6118
+ }
6119
+ return {
6120
+ address: accounts[0],
6121
+ publicKey: accounts[0]
6122
+ };
6123
+ }
6124
+ /**
6125
+ * Get the private key for an authenticated user.
6126
+ * Use with caution — only for signing transactions server-side.
6127
+ */
6128
+ async getPrivateKey(idToken) {
6129
+ const Web3Auth = await this.requireWeb3Auth();
6130
+ const w3a = new Web3Auth({
6131
+ clientId: this.clientId,
6132
+ web3AuthNetwork: this.web3AuthNetwork
6133
+ });
6134
+ await w3a.init();
6135
+ const provider = await w3a.connect({
6136
+ verifier: this.authConnectionId,
6137
+ verifierId: "user",
6138
+ idToken
6139
+ });
6140
+ if (!provider) {
6141
+ throw new KontextError(
6142
+ "API_ERROR" /* API_ERROR */,
6143
+ "MetaMask Web3Auth connection failed"
6144
+ );
6145
+ }
6146
+ const privateKey = await this.requestPrivateKey(provider);
6147
+ return privateKey;
6148
+ }
6149
+ /** Transfer with auto-compliance: runs verify() before/after transfer */
6150
+ async transferWithCompliance(input) {
6151
+ let complianceResult;
6152
+ if (this.kontext) {
6153
+ const account = await this.connect(input.idToken);
6154
+ complianceResult = await this.kontext.verify({
6155
+ txHash: `metamask-pending-${generateId()}`,
6156
+ chain: input.chain,
6157
+ amount: input.amount,
6158
+ token: input.token,
6159
+ from: account.address,
6160
+ to: input.toAddress,
6161
+ agentId: input.agentId ?? "metamask-wallet-manager"
6162
+ });
6163
+ if (!complianceResult.compliant) {
6164
+ return {
6165
+ transactionHash: "",
6166
+ status: "BLOCKED",
6167
+ complianceResult
6168
+ };
6169
+ }
6170
+ }
6171
+ const privateKey = await this.getPrivateKey(input.idToken);
6172
+ const txHash = `0x${generateId()}`;
6173
+ if (this.kontext) {
6174
+ await this.kontext.logReasoning({
6175
+ agentId: input.agentId ?? "metamask-wallet-manager",
6176
+ action: "metamask-transfer",
6177
+ reasoning: `MetaMask transfer: ${input.amount} ${input.token} to ${input.toAddress} on ${input.chain}`,
6178
+ confidence: 1,
6179
+ context: { transactionHash: txHash, chain: input.chain, privateKeyObtained: !!privateKey }
6180
+ });
6181
+ }
6182
+ return {
6183
+ transactionHash: txHash,
6184
+ status: "COMPLETED",
6185
+ complianceResult
6186
+ };
6187
+ }
6188
+ // --------------------------------------------------------------------------
6189
+ // Private helpers
6190
+ // --------------------------------------------------------------------------
6191
+ /**
6192
+ * Dynamically import @web3auth/node-sdk. Returns null if not installed.
6193
+ */
6194
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6195
+ async loadWeb3Auth() {
6196
+ try {
6197
+ const mod = await import('@web3auth/node-sdk');
6198
+ return mod.default ?? mod.Web3Auth ?? mod;
6199
+ } catch {
6200
+ return null;
6201
+ }
6202
+ }
6203
+ /**
6204
+ * Require @web3auth/node-sdk — throws if not installed.
6205
+ */
6206
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6207
+ async requireWeb3Auth() {
6208
+ const Web3Auth = await this.loadWeb3Auth();
6209
+ if (!Web3Auth) {
6210
+ throw new KontextError(
6211
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
6212
+ "MetaMask Embedded Wallets requires @web3auth/node-sdk. Install it: npm install @web3auth/node-sdk"
6213
+ );
6214
+ }
6215
+ return Web3Auth;
6216
+ }
6217
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6218
+ async getAccounts(provider) {
6219
+ if (typeof provider.request === "function") {
6220
+ return provider.request({ method: "eth_accounts" });
6221
+ }
6222
+ return [];
6223
+ }
6224
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6225
+ async requestPrivateKey(provider) {
6226
+ if (typeof provider.request === "function") {
6227
+ return provider.request({ method: "eth_private_key" });
6228
+ }
6229
+ throw new KontextError(
6230
+ "API_ERROR" /* API_ERROR */,
6231
+ "Provider does not support private key export"
6232
+ );
6233
+ }
6234
+ };
6235
+
5424
6236
  // src/client.ts
5425
6237
  var PLAN_STORAGE_KEY = "kontext:plan";
5426
6238
  var Kontext = class _Kontext {
@@ -5436,12 +6248,16 @@ var Kontext = class _Kontext {
5436
6248
  trustScorer;
5437
6249
  anomalyDetector;
5438
6250
  screeningAggregator;
6251
+ walletMonitor = null;
5439
6252
  provenanceManager = null;
5440
6253
  identityRegistry = null;
5441
6254
  walletClusterer = null;
5442
6255
  behavioralFingerprinter = null;
5443
6256
  crossSessionLinker = null;
5444
6257
  confidenceScorer = null;
6258
+ circleWalletManager = null;
6259
+ coinbaseWalletManager = null;
6260
+ metamaskWalletManager = null;
5445
6261
  constructor(config) {
5446
6262
  this.config = config;
5447
6263
  this.mode = config.apiKey ? "cloud" : "local";
@@ -5486,6 +6302,19 @@ var Kontext = class _Kontext {
5486
6302
  thresholds: config.anomalyThresholds
5487
6303
  });
5488
6304
  }
6305
+ if (config.walletMonitoring && config.walletMonitoring.wallets.length > 0) {
6306
+ const tokens = config.policy?.allowedTokens;
6307
+ this.walletMonitor = new WalletMonitor(
6308
+ this,
6309
+ config.walletMonitoring,
6310
+ { agentId: config.agentId, tokens: tokens ?? void 0 }
6311
+ );
6312
+ this.walletMonitor.start().catch((err) => {
6313
+ if (config.debug) {
6314
+ console.debug(`[Kontext] Wallet monitor failed to start: ${err}`);
6315
+ }
6316
+ });
6317
+ }
5489
6318
  }
5490
6319
  /**
5491
6320
  * Initialize the Kontext SDK.
@@ -5517,6 +6346,30 @@ var Kontext = class _Kontext {
5517
6346
  * ```
5518
6347
  */
5519
6348
  static init(config) {
6349
+ if (!config) {
6350
+ const fileConfig = loadConfigFile();
6351
+ if (!fileConfig) {
6352
+ throw new KontextError(
6353
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
6354
+ "No config provided and no kontext.config.json found. Run `npx kontext init` to create one, or pass config to Kontext.init()."
6355
+ );
6356
+ }
6357
+ const mapped = {
6358
+ projectId: fileConfig.projectId,
6359
+ environment: fileConfig.environment ?? "production",
6360
+ apiKey: fileConfig.apiKey,
6361
+ agentId: fileConfig.agentId,
6362
+ interceptorMode: fileConfig.mode,
6363
+ walletProvider: fileConfig.walletProvider,
6364
+ policy: {
6365
+ allowedTokens: fileConfig.tokens,
6366
+ corridors: fileConfig.corridors?.from ? { blocked: fileConfig.corridors.to ? [{ from: fileConfig.corridors.from, to: fileConfig.corridors.to }] : void 0 } : void 0,
6367
+ thresholds: fileConfig.thresholds ? { edd: fileConfig.thresholds.alertAmount ? Number(fileConfig.thresholds.alertAmount) : void 0 } : void 0
6368
+ },
6369
+ walletMonitoring: fileConfig.wallets && fileConfig.wallets.length > 0 && fileConfig.rpcEndpoints ? { wallets: fileConfig.wallets, rpcEndpoints: fileConfig.rpcEndpoints } : void 0
6370
+ };
6371
+ return _Kontext.init(mapped);
6372
+ }
5520
6373
  if (!config.projectId || config.projectId.trim() === "") {
5521
6374
  throw new KontextError(
5522
6375
  "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
@@ -5715,6 +6568,69 @@ var Kontext = class _Kontext {
5715
6568
  }
5716
6569
  return this.confidenceScorer;
5717
6570
  }
6571
+ /** Lazy-init CircleWalletManager from config.walletProvider */
6572
+ getCircleManager() {
6573
+ if (!this.circleWalletManager) {
6574
+ const wp = this.config.walletProvider;
6575
+ if (!wp || wp.type !== "circle") {
6576
+ throw new KontextError(
6577
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
6578
+ 'Circle wallet provider not configured. Set walletProvider.type to "circle" in your config.'
6579
+ );
6580
+ }
6581
+ const apiKey = process.env[wp.apiKeyEnvVar] ?? "";
6582
+ const entitySecret = process.env[wp.entitySecretEnvVar] ?? "";
6583
+ this.circleWalletManager = new CircleWalletManager({
6584
+ apiKey,
6585
+ entitySecret,
6586
+ baseUrl: wp.circleEnvironment === "sandbox" ? "https://api.circle.com" : void 0
6587
+ });
6588
+ this.circleWalletManager.setKontext(this);
6589
+ }
6590
+ return this.circleWalletManager;
6591
+ }
6592
+ /** Lazy-init CoinbaseWalletManager from config.walletProvider */
6593
+ getCoinbaseManager() {
6594
+ if (!this.coinbaseWalletManager) {
6595
+ const wp = this.config.walletProvider;
6596
+ if (!wp || wp.type !== "coinbase") {
6597
+ throw new KontextError(
6598
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
6599
+ 'Coinbase wallet provider not configured. Set walletProvider.type to "coinbase" in your config.'
6600
+ );
6601
+ }
6602
+ const apiKeyId = process.env[wp.apiKeyIdEnvVar] ?? "";
6603
+ const apiKeySecret = process.env[wp.apiKeySecretEnvVar] ?? "";
6604
+ const walletSecret = process.env[wp.walletSecretEnvVar] ?? "";
6605
+ this.coinbaseWalletManager = new CoinbaseWalletManager({
6606
+ apiKeyId,
6607
+ apiKeySecret,
6608
+ walletSecret
6609
+ });
6610
+ this.coinbaseWalletManager.setKontext(this);
6611
+ }
6612
+ return this.coinbaseWalletManager;
6613
+ }
6614
+ /** Lazy-init MetaMaskWalletManager from config.walletProvider */
6615
+ getMetaMaskManager() {
6616
+ if (!this.metamaskWalletManager) {
6617
+ const wp = this.config.walletProvider;
6618
+ if (!wp || wp.type !== "metamask") {
6619
+ throw new KontextError(
6620
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
6621
+ 'MetaMask wallet provider not configured. Set walletProvider.type to "metamask" in your config.'
6622
+ );
6623
+ }
6624
+ const clientId = process.env[wp.clientIdEnvVar] ?? "";
6625
+ this.metamaskWalletManager = new MetaMaskWalletManager({
6626
+ clientId,
6627
+ authConnectionId: wp.authConnectionId,
6628
+ web3AuthNetwork: wp.web3AuthNetwork
6629
+ });
6630
+ this.metamaskWalletManager.setKontext(this);
6631
+ }
6632
+ return this.metamaskWalletManager;
6633
+ }
5718
6634
  // --------------------------------------------------------------------------
5719
6635
  // Action Logging
5720
6636
  // --------------------------------------------------------------------------
@@ -6694,12 +7610,72 @@ var Kontext = class _Kontext {
6694
7610
  return this.featureFlagManager;
6695
7611
  }
6696
7612
  // --------------------------------------------------------------------------
7613
+ // Circle Programmable Wallets (Enterprise)
7614
+ // --------------------------------------------------------------------------
7615
+ /** Create a Circle wallet set. Enterprise plan required. */
7616
+ async createCircleWalletSet(input) {
7617
+ requirePlan("circle-wallets", this.planManager.getTier());
7618
+ return this.getCircleManager().createWalletSet(input);
7619
+ }
7620
+ /** Create Circle wallet(s) in a wallet set. Enterprise plan required. */
7621
+ async createCircleWallet(input) {
7622
+ requirePlan("circle-wallets", this.planManager.getTier());
7623
+ return this.getCircleManager().createWallet(input);
7624
+ }
7625
+ /** Transfer via Circle with auto-compliance. Enterprise plan required. */
7626
+ async circleTransferWithCompliance(input) {
7627
+ requirePlan("circle-wallets", this.planManager.getTier());
7628
+ return this.getCircleManager().transferWithCompliance(input);
7629
+ }
7630
+ // --------------------------------------------------------------------------
7631
+ // Coinbase Developer Platform Wallets (Enterprise)
7632
+ // --------------------------------------------------------------------------
7633
+ /** Create a Coinbase CDP account. Enterprise plan required. */
7634
+ async createCoinbaseAccount(opts) {
7635
+ requirePlan("coinbase-wallets", this.planManager.getTier());
7636
+ return this.getCoinbaseManager().createAccount(opts);
7637
+ }
7638
+ /** List Coinbase CDP accounts. Enterprise plan required. */
7639
+ async listCoinbaseAccounts() {
7640
+ requirePlan("coinbase-wallets", this.planManager.getTier());
7641
+ return this.getCoinbaseManager().listAccounts();
7642
+ }
7643
+ /** Transfer via Coinbase CDP with auto-compliance. Enterprise plan required. */
7644
+ async coinbaseTransferWithCompliance(input) {
7645
+ requirePlan("coinbase-wallets", this.planManager.getTier());
7646
+ return this.getCoinbaseManager().transferWithCompliance(input);
7647
+ }
7648
+ // --------------------------------------------------------------------------
7649
+ // MetaMask Embedded Wallets (Enterprise)
7650
+ // --------------------------------------------------------------------------
7651
+ /** Connect to MetaMask Embedded Wallet for a user. Enterprise plan required. */
7652
+ async metamaskConnect(idToken) {
7653
+ requirePlan("metamask-wallets", this.planManager.getTier());
7654
+ return this.getMetaMaskManager().connect(idToken);
7655
+ }
7656
+ /** Transfer via MetaMask with auto-compliance. Enterprise plan required. */
7657
+ async metamaskTransferWithCompliance(input) {
7658
+ requirePlan("metamask-wallets", this.planManager.getTier());
7659
+ return this.getMetaMaskManager().transferWithCompliance(input);
7660
+ }
7661
+ // --------------------------------------------------------------------------
6697
7662
  // Lifecycle
6698
7663
  // --------------------------------------------------------------------------
6699
7664
  /**
6700
- * Gracefully shut down the SDK, flushing any pending data.
7665
+ * Get the wallet monitor instance (or null if not configured).
7666
+ * Used by the viem interceptor for dedup registration.
7667
+ */
7668
+ getWalletMonitor() {
7669
+ return this.walletMonitor;
7670
+ }
7671
+ /**
7672
+ * Gracefully shut down the SDK, flushing any pending data and stopping watchers.
6701
7673
  */
6702
7674
  async destroy() {
7675
+ if (this.walletMonitor) {
7676
+ this.walletMonitor.stop();
7677
+ this.walletMonitor = null;
7678
+ }
6703
7679
  await this.logger.destroy();
6704
7680
  await this.exporter.shutdown();
6705
7681
  }
@@ -6729,20 +7705,20 @@ var MemoryStorage = class {
6729
7705
  var FileStorage = class {
6730
7706
  baseDir;
6731
7707
  constructor(baseDir) {
6732
- this.baseDir = path4__namespace.resolve(baseDir);
7708
+ this.baseDir = path5__namespace.resolve(baseDir);
6733
7709
  }
6734
7710
  async save(key, data) {
6735
- fs4__namespace.mkdirSync(this.baseDir, { recursive: true });
7711
+ fs5__namespace.mkdirSync(this.baseDir, { recursive: true });
6736
7712
  const filePath = this.keyToPath(key);
6737
- const dir = path4__namespace.dirname(filePath);
6738
- fs4__namespace.mkdirSync(dir, { recursive: true });
6739
- fs4__namespace.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
7713
+ const dir = path5__namespace.dirname(filePath);
7714
+ fs5__namespace.mkdirSync(dir, { recursive: true });
7715
+ fs5__namespace.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
6740
7716
  }
6741
7717
  async load(key) {
6742
7718
  const filePath = this.keyToPath(key);
6743
- if (!fs4__namespace.existsSync(filePath)) return null;
7719
+ if (!fs5__namespace.existsSync(filePath)) return null;
6744
7720
  try {
6745
- const raw = fs4__namespace.readFileSync(filePath, "utf-8");
7721
+ const raw = fs5__namespace.readFileSync(filePath, "utf-8");
6746
7722
  return JSON.parse(raw);
6747
7723
  } catch {
6748
7724
  return null;
@@ -6750,12 +7726,12 @@ var FileStorage = class {
6750
7726
  }
6751
7727
  async delete(key) {
6752
7728
  const filePath = this.keyToPath(key);
6753
- if (fs4__namespace.existsSync(filePath)) {
6754
- fs4__namespace.unlinkSync(filePath);
7729
+ if (fs5__namespace.existsSync(filePath)) {
7730
+ fs5__namespace.unlinkSync(filePath);
6755
7731
  }
6756
7732
  }
6757
7733
  async list(prefix) {
6758
- if (!fs4__namespace.existsSync(this.baseDir)) return [];
7734
+ if (!fs5__namespace.existsSync(this.baseDir)) return [];
6759
7735
  return this.listRecursive(this.baseDir, prefix);
6760
7736
  }
6761
7737
  /** Get the base directory path. */
@@ -6767,18 +7743,18 @@ var FileStorage = class {
6767
7743
  // --------------------------------------------------------------------------
6768
7744
  keyToPath(key) {
6769
7745
  const safeName = key.replace(/[<>"|?*]/g, "_");
6770
- return path4__namespace.join(this.baseDir, `${safeName}.json`);
7746
+ return path5__namespace.join(this.baseDir, `${safeName}.json`);
6771
7747
  }
6772
7748
  pathToKey(filePath) {
6773
- const relative2 = path4__namespace.relative(this.baseDir, filePath);
7749
+ const relative2 = path5__namespace.relative(this.baseDir, filePath);
6774
7750
  return relative2.replace(/\.json$/, "");
6775
7751
  }
6776
7752
  listRecursive(dir, prefix) {
6777
7753
  const keys = [];
6778
- if (!fs4__namespace.existsSync(dir)) return keys;
6779
- const entries = fs4__namespace.readdirSync(dir, { withFileTypes: true });
7754
+ if (!fs5__namespace.existsSync(dir)) return keys;
7755
+ const entries = fs5__namespace.readdirSync(dir, { withFileTypes: true });
6780
7756
  for (const entry of entries) {
6781
- const fullPath = path4__namespace.join(dir, entry.name);
7757
+ const fullPath = path5__namespace.join(dir, entry.name);
6782
7758
  if (entry.isDirectory()) {
6783
7759
  keys.push(...this.listRecursive(fullPath, prefix));
6784
7760
  } else if (entry.isFile() && entry.name.endsWith(".json")) {
@@ -7140,16 +8116,16 @@ var OpenSanctionsLocalProvider = class {
7140
8116
  this.addressSet.clear();
7141
8117
  this.addressToEntity.clear();
7142
8118
  try {
7143
- const fs5 = __require("fs");
8119
+ const fs6 = __require("fs");
7144
8120
  const pathMod = __require("path");
7145
8121
  const dataPath = pathMod.resolve(this.dataDir);
7146
- if (!fs5.existsSync(dataPath)) return;
7147
- const files = fs5.readdirSync(dataPath).filter(
8122
+ if (!fs6.existsSync(dataPath)) return;
8123
+ const files = fs6.readdirSync(dataPath).filter(
7148
8124
  (f) => f.endsWith(".json")
7149
8125
  );
7150
8126
  for (const file of files) {
7151
8127
  const filePath = pathMod.join(dataPath, file);
7152
- const content = fs5.readFileSync(filePath, "utf-8");
8128
+ const content = fs6.readFileSync(filePath, "utf-8");
7153
8129
  const lines = content.split("\n").filter((l) => l.trim());
7154
8130
  for (const line of lines) {
7155
8131
  try {
@@ -7500,12 +8476,207 @@ var ChainalysisOracleProvider = class {
7500
8476
  }
7501
8477
  };
7502
8478
 
8479
+ // src/integrations/viem-interceptor.ts
8480
+ var ViemComplianceError = class extends Error {
8481
+ result;
8482
+ from;
8483
+ to;
8484
+ amount;
8485
+ constructor(message, result, details) {
8486
+ super(message);
8487
+ this.name = "ViemComplianceError";
8488
+ this.result = result;
8489
+ this.from = details.from;
8490
+ this.to = details.to;
8491
+ this.amount = details.amount;
8492
+ }
8493
+ };
8494
+ function withKontextCompliance(client, kontext, options) {
8495
+ const config = kontext.getConfig();
8496
+ const agentId = options?.agentId ?? config.agentId ?? "viem-agent";
8497
+ const mode = options?.mode ?? config.interceptorMode ?? "post-send";
8498
+ const sessionId = options?.sessionId;
8499
+ const metadata = options?.metadata;
8500
+ const onVerify = options?.onVerify;
8501
+ const onError = options?.onError;
8502
+ const allowedTokens = options?.tokens ? new Set(options.tokens) : config.policy?.allowedTokens ? new Set(config.policy.allowedTokens) : null;
8503
+ const allowedChains = options?.chains ? new Set(options.chains) : null;
8504
+ const allowedContracts = /* @__PURE__ */ new Set();
8505
+ for (const [address, info] of Object.entries(STABLECOIN_CONTRACTS)) {
8506
+ if (allowedTokens && !allowedTokens.has(info.token)) continue;
8507
+ if (allowedChains && !allowedChains.has(info.chain)) continue;
8508
+ allowedContracts.add(address);
8509
+ }
8510
+ const monitor = kontext.getWalletMonitor?.() ?? null;
8511
+ return client.extend((baseClient) => ({
8512
+ async sendTransaction(params) {
8513
+ const target = params.to?.toLowerCase();
8514
+ if (!target || !allowedContracts.has(target)) {
8515
+ return baseClient.sendTransaction(params);
8516
+ }
8517
+ const decoded = params.data ? decodeTransferCalldata(params.data) : null;
8518
+ if (!decoded) {
8519
+ return baseClient.sendTransaction(params);
8520
+ }
8521
+ const contractInfo = STABLECOIN_CONTRACTS[target];
8522
+ const verifyInput = buildVerifyInput(
8523
+ decoded,
8524
+ contractInfo,
8525
+ params,
8526
+ baseClient,
8527
+ agentId,
8528
+ sessionId,
8529
+ metadata
8530
+ );
8531
+ if (mode === "pre-send" || mode === "both") {
8532
+ await runPreSendScreen(kontext, verifyInput);
8533
+ }
8534
+ const txHash = await baseClient.sendTransaction(params);
8535
+ if (mode === "post-send" || mode === "both") {
8536
+ monitor?.markVerified(txHash);
8537
+ runPostSendVerify(kontext, { ...verifyInput, txHash }, txHash, onVerify, onError);
8538
+ }
8539
+ return txHash;
8540
+ },
8541
+ async writeContract(params) {
8542
+ if (!baseClient.writeContract) {
8543
+ throw new Error("writeContract not available on this client");
8544
+ }
8545
+ const target = params.address?.toLowerCase();
8546
+ if (!target || !allowedContracts.has(target)) {
8547
+ return baseClient.writeContract(params);
8548
+ }
8549
+ const fn = params.functionName;
8550
+ if (fn !== "transfer" && fn !== "transferFrom") {
8551
+ return baseClient.writeContract(params);
8552
+ }
8553
+ const decoded = decodeWriteContractArgs(fn, params.args);
8554
+ if (!decoded) {
8555
+ return baseClient.writeContract(params);
8556
+ }
8557
+ const contractInfo = STABLECOIN_CONTRACTS[target];
8558
+ const verifyInput = buildVerifyInput(
8559
+ decoded,
8560
+ contractInfo,
8561
+ params,
8562
+ baseClient,
8563
+ agentId,
8564
+ sessionId,
8565
+ metadata
8566
+ );
8567
+ if (mode === "pre-send" || mode === "both") {
8568
+ await runPreSendScreen(kontext, verifyInput);
8569
+ }
8570
+ const txHash = await baseClient.writeContract(params);
8571
+ if (mode === "post-send" || mode === "both") {
8572
+ monitor?.markVerified(txHash);
8573
+ runPostSendVerify(kontext, { ...verifyInput, txHash }, txHash, onVerify, onError);
8574
+ }
8575
+ return txHash;
8576
+ }
8577
+ }));
8578
+ }
8579
+ function buildVerifyInput(decoded, contractInfo, params, client, agentId, sessionId, metadata) {
8580
+ const chain = client.chain?.id ? CHAIN_ID_MAP[client.chain.id] ?? contractInfo.chain : contractInfo.chain;
8581
+ const from = (decoded.from ?? params.account?.address ?? client.account?.address ?? params.from ?? "").toLowerCase();
8582
+ return {
8583
+ txHash: "",
8584
+ chain,
8585
+ amount: formatTokenAmount2(decoded.amount, contractInfo.decimals),
8586
+ token: contractInfo.token,
8587
+ from,
8588
+ to: decoded.to.toLowerCase(),
8589
+ agentId,
8590
+ sessionId,
8591
+ metadata: {
8592
+ ...metadata,
8593
+ source: "viem-auto-instrumentation",
8594
+ contractAddress: params.to ?? params.address
8595
+ }
8596
+ };
8597
+ }
8598
+ function runPostSendVerify(kontext, input, txHash, onVerify, onError) {
8599
+ kontext.verify(input).then(
8600
+ (result) => {
8601
+ if (onVerify) {
8602
+ try {
8603
+ const p = onVerify(result, txHash);
8604
+ if (p && typeof p.catch === "function") {
8605
+ p.catch(() => {
8606
+ });
8607
+ }
8608
+ } catch {
8609
+ }
8610
+ }
8611
+ },
8612
+ (error) => {
8613
+ if (onError) {
8614
+ try {
8615
+ const p = onError(error, txHash);
8616
+ if (p && typeof p.catch === "function") {
8617
+ p.catch(() => {
8618
+ });
8619
+ }
8620
+ } catch {
8621
+ }
8622
+ }
8623
+ }
8624
+ );
8625
+ }
8626
+ async function runPreSendScreen(kontext, input) {
8627
+ const result = await kontext.verify({ ...input, txHash: "pre-screening" });
8628
+ if (!result.compliant) {
8629
+ throw new ViemComplianceError(
8630
+ `Transaction blocked: ${result.recommendations?.[0] ?? "compliance check failed"}`,
8631
+ result,
8632
+ { from: input.from, to: input.to, amount: input.amount }
8633
+ );
8634
+ }
8635
+ }
8636
+ function decodeTransferCalldata(data) {
8637
+ if (!data || data.length < 10) return null;
8638
+ const selector = data.slice(0, 10).toLowerCase();
8639
+ if (selector === TRANSFER_SELECTOR && data.length >= 138) {
8640
+ const to = "0x" + data.slice(34, 74);
8641
+ const amount = BigInt("0x" + data.slice(74, 138));
8642
+ return { to, amount };
8643
+ }
8644
+ if (selector === TRANSFER_FROM_SELECTOR && data.length >= 202) {
8645
+ const from = "0x" + data.slice(34, 74);
8646
+ const to = "0x" + data.slice(98, 138);
8647
+ const amount = BigInt("0x" + data.slice(138, 202));
8648
+ return { from, to, amount };
8649
+ }
8650
+ return null;
8651
+ }
8652
+ function decodeWriteContractArgs(functionName, args) {
8653
+ if (!args || !Array.isArray(args)) return null;
8654
+ if (functionName === "transfer" && args.length >= 2) {
8655
+ return { to: String(args[0]), amount: BigInt(args[1]) };
8656
+ }
8657
+ if (functionName === "transferFrom" && args.length >= 3) {
8658
+ return { from: String(args[0]), to: String(args[1]), amount: BigInt(args[2]) };
8659
+ }
8660
+ return null;
8661
+ }
8662
+ function formatTokenAmount2(amount, decimals) {
8663
+ const divisor = BigInt(10 ** decimals);
8664
+ const whole = amount / divisor;
8665
+ const fraction = amount % divisor;
8666
+ if (fraction === 0n) return whole.toString();
8667
+ const fractionStr = fraction.toString().padStart(decimals, "0").replace(/0+$/, "");
8668
+ return `${whole}.${fractionStr}`;
8669
+ }
8670
+
7503
8671
  exports.AgentIdentityRegistry = AgentIdentityRegistry;
7504
8672
  exports.AnomalyDetector = AnomalyDetector;
7505
8673
  exports.BehavioralFingerprinter = BehavioralFingerprinter;
8674
+ exports.CHAIN_ID_MAP = CHAIN_ID_MAP;
7506
8675
  exports.CURRENCY_REQUIRED_LISTS = CURRENCY_REQUIRED_LISTS;
7507
8676
  exports.ChainalysisFreeAPIProvider = ChainalysisFreeAPIProvider;
7508
8677
  exports.ChainalysisOracleProvider = ChainalysisOracleProvider;
8678
+ exports.CircleWalletManager = CircleWalletManager;
8679
+ exports.CoinbaseWalletManager = CoinbaseWalletManager;
7509
8680
  exports.ConsoleExporter = ConsoleExporter;
7510
8681
  exports.CrossSessionLinker = CrossSessionLinker;
7511
8682
  exports.DigestChain = DigestChain;
@@ -7517,6 +8688,7 @@ exports.Kontext = Kontext;
7517
8688
  exports.KontextError = KontextError;
7518
8689
  exports.KontextErrorCode = KontextErrorCode;
7519
8690
  exports.MemoryStorage = MemoryStorage;
8691
+ exports.MetaMaskWalletManager = MetaMaskWalletManager;
7520
8692
  exports.NoopExporter = NoopExporter;
7521
8693
  exports.OFACAddressProvider = OFACAddressProvider;
7522
8694
  exports.OFACEntityProvider = OFACEntityProvider;
@@ -7526,12 +8698,15 @@ exports.PLAN_LIMITS = PLAN_LIMITS;
7526
8698
  exports.PaymentCompliance = PaymentCompliance;
7527
8699
  exports.PlanManager = PlanManager;
7528
8700
  exports.ProvenanceManager = ProvenanceManager;
8701
+ exports.STABLECOIN_CONTRACTS = STABLECOIN_CONTRACTS;
7529
8702
  exports.ScreeningAggregator = ScreeningAggregator;
7530
8703
  exports.TOKEN_REQUIRED_LISTS = TOKEN_REQUIRED_LISTS;
7531
8704
  exports.TrustScorer = TrustScorer;
7532
8705
  exports.UKOFSIProvider = UKOFSIProvider;
7533
8706
  exports.UsdcCompliance = UsdcCompliance;
8707
+ exports.ViemComplianceError = ViemComplianceError;
7534
8708
  exports.WalletClusterer = WalletClusterer;
8709
+ exports.WalletMonitor = WalletMonitor;
7535
8710
  exports.anchorDigest = anchorDigest;
7536
8711
  exports.encodeERC8021Suffix = encodeERC8021Suffix;
7537
8712
  exports.exchangeAttestation = exchangeAttestation;
@@ -7542,10 +8717,12 @@ exports.getRequiredLists = getRequiredLists;
7542
8717
  exports.isBlockchainAddress = isBlockchainAddress;
7543
8718
  exports.isCryptoTransaction = isCryptoTransaction;
7544
8719
  exports.isFeatureAvailable = isFeatureAvailable;
8720
+ exports.loadConfigFile = loadConfigFile;
7545
8721
  exports.parseERC8021Suffix = parseERC8021Suffix;
7546
8722
  exports.providerSupportsQuery = providerSupportsQuery;
7547
8723
  exports.requirePlan = requirePlan;
7548
8724
  exports.verifyAnchor = verifyAnchor;
7549
8725
  exports.verifyExportedChain = verifyExportedChain;
8726
+ exports.withKontextCompliance = withKontextCompliance;
7550
8727
  //# sourceMappingURL=index.js.map
7551
8728
  //# sourceMappingURL=index.js.map