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.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createHash } from 'crypto';
2
- import * as fs4 from 'fs';
3
- import * as path4 from 'path';
2
+ import * as fs5 from 'fs';
3
+ import * as path5 from 'path';
4
4
 
5
5
  var __defProp = Object.defineProperty;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -1951,13 +1951,13 @@ var ActionLogger = class {
1951
1951
  }
1952
1952
  flushToFile(actions) {
1953
1953
  const outputDir = this.config.localOutputDir ?? ".kontext";
1954
- const logDir = path4.join(outputDir, "logs");
1954
+ const logDir = path5.join(outputDir, "logs");
1955
1955
  try {
1956
- fs4.mkdirSync(logDir, { recursive: true });
1956
+ fs5.mkdirSync(logDir, { recursive: true });
1957
1957
  const filename = `actions-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.jsonl`;
1958
- const filePath = path4.join(logDir, filename);
1958
+ const filePath = path5.join(logDir, filename);
1959
1959
  const lines = actions.map((a) => JSON.stringify(a)).join("\n") + "\n";
1960
- fs4.appendFileSync(filePath, lines, "utf-8");
1960
+ fs5.appendFileSync(filePath, lines, "utf-8");
1961
1961
  } catch (error) {
1962
1962
  this.emitLog("warn", "Failed to write log file", { error });
1963
1963
  }
@@ -2567,9 +2567,9 @@ var SANCTIONED_SET = new Set(
2567
2567
  function loadCachedSDN() {
2568
2568
  try {
2569
2569
  const dataDir = process.env["KONTEXT_DATA_DIR"] || ".kontext";
2570
- const cachePath = path4.join(dataDir, "ofac-sdn-cache.json");
2571
- if (fs4.existsSync(cachePath)) {
2572
- const cache = JSON.parse(fs4.readFileSync(cachePath, "utf-8"));
2570
+ const cachePath = path5.join(dataDir, "ofac-sdn-cache.json");
2571
+ if (fs5.existsSync(cachePath)) {
2572
+ const cache = JSON.parse(fs5.readFileSync(cachePath, "utf-8"));
2573
2573
  if (Array.isArray(cache.addresses)) {
2574
2574
  for (const addr of cache.addresses) {
2575
2575
  SANCTIONED_SET.add(String(addr).toLowerCase());
@@ -3625,7 +3625,9 @@ var FEATURE_MIN_PLAN = {
3625
3625
  "unified-screening": "pro",
3626
3626
  "blocklist-manager": "pro",
3627
3627
  "kya-identity": "pro",
3628
- "kya-behavioral": "enterprise"
3628
+ "kya-behavioral": "enterprise",
3629
+ "coinbase-wallets": "enterprise",
3630
+ "metamask-wallets": "enterprise"
3629
3631
  };
3630
3632
  var PLAN_RANK = { free: 0, pro: 1, enterprise: 2 };
3631
3633
  var FEATURE_LABELS = {
@@ -3644,7 +3646,9 @@ var FEATURE_LABELS = {
3644
3646
  "unified-screening": "Unified screening (OFAC, Chainalysis, OpenSanctions)",
3645
3647
  "blocklist-manager": "Custom blocklist/allowlist manager",
3646
3648
  "kya-identity": "KYA identity resolution (declared identity, wallet clustering)",
3647
- "kya-behavioral": "KYA behavioral fingerprinting (cross-session linking, confidence scoring)"
3649
+ "kya-behavioral": "KYA behavioral fingerprinting (cross-session linking, confidence scoring)",
3650
+ "coinbase-wallets": "Coinbase Developer Platform Wallets",
3651
+ "metamask-wallets": "MetaMask Embedded Wallets"
3648
3652
  };
3649
3653
  function isFeatureAvailable(feature, currentPlan) {
3650
3654
  const requiredPlan = FEATURE_MIN_PLAN[feature];
@@ -3690,7 +3694,7 @@ var JsonFileExporter = class {
3690
3694
  buffer = [];
3691
3695
  bufferSize;
3692
3696
  constructor(options) {
3693
- this.outputDir = path4.resolve(options?.outputDir ?? ".kontext/exports");
3697
+ this.outputDir = path5.resolve(options?.outputDir ?? ".kontext/exports");
3694
3698
  this.bufferSize = options?.bufferSize ?? 1;
3695
3699
  }
3696
3700
  async export(events) {
@@ -3705,11 +3709,11 @@ var JsonFileExporter = class {
3705
3709
  const toWrite = [...this.buffer];
3706
3710
  this.buffer = [];
3707
3711
  try {
3708
- fs4.mkdirSync(this.outputDir, { recursive: true });
3712
+ fs5.mkdirSync(this.outputDir, { recursive: true });
3709
3713
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
3710
- const filePath = path4.join(this.outputDir, `events-${date}.jsonl`);
3714
+ const filePath = path5.join(this.outputDir, `events-${date}.jsonl`);
3711
3715
  const lines = toWrite.map((e) => JSON.stringify(e)).join("\n") + "\n";
3712
- fs4.appendFileSync(filePath, lines, "utf-8");
3716
+ fs5.appendFileSync(filePath, lines, "utf-8");
3713
3717
  } catch (error) {
3714
3718
  console.warn("[Kontext JsonFileExporter] Failed to write events:", error);
3715
3719
  }
@@ -3879,6 +3883,218 @@ function extractDocumentId(resourceName) {
3879
3883
  const parts = resourceName.split("/");
3880
3884
  return parts[parts.length - 1] ?? resourceName;
3881
3885
  }
3886
+ var CONFIG_FILENAME = "kontext.config.json";
3887
+ function loadConfigFile(startDir) {
3888
+ const dir = startDir ?? process.cwd();
3889
+ const filePath = findConfigFile(dir);
3890
+ if (!filePath) return null;
3891
+ try {
3892
+ const raw = fs5.readFileSync(filePath, "utf-8");
3893
+ const parsed = JSON.parse(raw);
3894
+ if (!parsed.projectId || typeof parsed.projectId !== "string") {
3895
+ return null;
3896
+ }
3897
+ return parsed;
3898
+ } catch {
3899
+ return null;
3900
+ }
3901
+ }
3902
+ function findConfigFile(dir) {
3903
+ let current = path5.resolve(dir);
3904
+ const root = path5.parse(current).root;
3905
+ while (true) {
3906
+ const candidate = path5.join(current, CONFIG_FILENAME);
3907
+ if (fs5.existsSync(candidate)) {
3908
+ return candidate;
3909
+ }
3910
+ const parent = path5.dirname(current);
3911
+ if (parent === current || current === root) {
3912
+ return null;
3913
+ }
3914
+ current = parent;
3915
+ }
3916
+ }
3917
+
3918
+ // src/integrations/data/stablecoin-contracts.ts
3919
+ var STABLECOIN_CONTRACTS = {
3920
+ // USDC (6 decimals)
3921
+ "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": { token: "USDC", chain: "ethereum", decimals: 6 },
3922
+ "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913": { token: "USDC", chain: "base", decimals: 6 },
3923
+ "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359": { token: "USDC", chain: "polygon", decimals: 6 },
3924
+ "0xaf88d065e77c8cc2239327c5edb3a432268e5831": { token: "USDC", chain: "arbitrum", decimals: 6 },
3925
+ "0x0b2c639c533813f4aa9d7837caf62653d097ff85": { token: "USDC", chain: "optimism", decimals: 6 },
3926
+ "0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e": { token: "USDC", chain: "avalanche", decimals: 6 },
3927
+ // USDT (6 decimals)
3928
+ "0xdac17f958d2ee523a2206206994597c13d831ec7": { token: "USDT", chain: "ethereum", decimals: 6 },
3929
+ "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9": { token: "USDT", chain: "arbitrum", decimals: 6 },
3930
+ "0x94b008aa00579c1307b0ef2c499ad98a8ce58e58": { token: "USDT", chain: "optimism", decimals: 6 },
3931
+ "0xc2132d05d31c914a87c6611c10748aeb04b58e8f": { token: "USDT", chain: "polygon", decimals: 6 },
3932
+ // DAI (18 decimals)
3933
+ "0x6b175474e89094c44da98b954eedeac495271d0f": { token: "DAI", chain: "ethereum", decimals: 18 },
3934
+ "0x50c5725949a6f0c72e6c4a641f24049a917db0cb": { token: "DAI", chain: "base", decimals: 18 },
3935
+ // EURC (6 decimals)
3936
+ "0x1abaea1f7c830bd89acc67ec4af516284b1bc33c": { token: "EURC", chain: "ethereum", decimals: 6 },
3937
+ "0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42": { token: "EURC", chain: "base", decimals: 6 }
3938
+ };
3939
+ new Set(Object.keys(STABLECOIN_CONTRACTS));
3940
+ var CHAIN_ID_MAP = {
3941
+ 1: "ethereum",
3942
+ 8453: "base",
3943
+ 137: "polygon",
3944
+ 42161: "arbitrum",
3945
+ 10: "optimism",
3946
+ 43114: "avalanche"
3947
+ };
3948
+ var TRANSFER_SELECTOR = "0xa9059cbb";
3949
+ var TRANSFER_FROM_SELECTOR = "0x23b872dd";
3950
+ var TRANSFER_EVENT_ABI = {
3951
+ type: "event",
3952
+ name: "Transfer",
3953
+ inputs: [
3954
+ { type: "address", name: "from", indexed: true },
3955
+ { type: "address", name: "to", indexed: true },
3956
+ { type: "uint256", name: "value", indexed: false }
3957
+ ]
3958
+ };
3959
+
3960
+ // src/integrations/wallet-monitor.ts
3961
+ var WalletMonitor = class {
3962
+ kontext;
3963
+ config;
3964
+ agentId;
3965
+ tokens;
3966
+ unwatchers = [];
3967
+ running = false;
3968
+ /** Shared dedup set — tracks recently verified txHashes (populated by both layers) */
3969
+ verifiedTxHashes = /* @__PURE__ */ new Set();
3970
+ cleanupTimer = null;
3971
+ txTimestamps = /* @__PURE__ */ new Map();
3972
+ constructor(kontext, config, options) {
3973
+ this.kontext = kontext;
3974
+ this.config = config;
3975
+ this.agentId = options?.agentId ?? "wallet-monitor";
3976
+ this.tokens = options?.tokens ? new Set(options.tokens) : null;
3977
+ }
3978
+ /**
3979
+ * Mark a txHash as already verified (called by the viem interceptor layer).
3980
+ * The monitor will skip this tx if it later sees it on-chain.
3981
+ */
3982
+ markVerified(txHash) {
3983
+ const lower = txHash.toLowerCase();
3984
+ this.verifiedTxHashes.add(lower);
3985
+ this.txTimestamps.set(lower, Date.now());
3986
+ }
3987
+ /**
3988
+ * Start watching all configured chains for stablecoin transfers.
3989
+ * Dynamically imports viem — requires viem as a peer dependency.
3990
+ */
3991
+ async start() {
3992
+ if (this.running) return;
3993
+ let viem;
3994
+ try {
3995
+ viem = await import('viem');
3996
+ } catch {
3997
+ throw new Error(
3998
+ "Wallet monitoring requires viem. Install it: npm install viem"
3999
+ );
4000
+ }
4001
+ const { createPublicClient, http } = viem;
4002
+ const wallets = this.config.wallets.map((w) => w.toLowerCase());
4003
+ const pollingInterval = this.config.pollingIntervalMs ?? 12e3;
4004
+ const contractsByChain = /* @__PURE__ */ new Map();
4005
+ for (const [address, info] of Object.entries(STABLECOIN_CONTRACTS)) {
4006
+ if (this.tokens && !this.tokens.has(info.token)) continue;
4007
+ const existing = contractsByChain.get(info.chain) ?? [];
4008
+ existing.push({ address, info });
4009
+ contractsByChain.set(info.chain, existing);
4010
+ }
4011
+ for (const [chain, contracts] of contractsByChain) {
4012
+ const rpcUrl = this.config.rpcEndpoints[chain];
4013
+ if (!rpcUrl) continue;
4014
+ const client = createPublicClient({
4015
+ transport: http(rpcUrl)
4016
+ });
4017
+ for (const { address, info } of contracts) {
4018
+ const unwatch = client.watchEvent({
4019
+ address,
4020
+ event: TRANSFER_EVENT_ABI,
4021
+ args: { from: wallets.length === 1 ? wallets[0] : wallets },
4022
+ poll: true,
4023
+ pollingInterval,
4024
+ onLogs: (logs) => {
4025
+ for (const log of logs) {
4026
+ this.handleTransferLog(log, info);
4027
+ }
4028
+ }
4029
+ });
4030
+ this.unwatchers.push(unwatch);
4031
+ }
4032
+ }
4033
+ this.cleanupTimer = setInterval(() => {
4034
+ const cutoff = Date.now() - 5 * 60 * 1e3;
4035
+ for (const [hash, ts] of this.txTimestamps) {
4036
+ if (ts < cutoff) {
4037
+ this.verifiedTxHashes.delete(hash);
4038
+ this.txTimestamps.delete(hash);
4039
+ }
4040
+ }
4041
+ }, 6e4);
4042
+ this.running = true;
4043
+ }
4044
+ /** Stop all watchers and cleanup */
4045
+ stop() {
4046
+ for (const unwatch of this.unwatchers) {
4047
+ try {
4048
+ unwatch();
4049
+ } catch {
4050
+ }
4051
+ }
4052
+ this.unwatchers.length = 0;
4053
+ if (this.cleanupTimer) {
4054
+ clearInterval(this.cleanupTimer);
4055
+ this.cleanupTimer = null;
4056
+ }
4057
+ this.running = false;
4058
+ }
4059
+ isRunning() {
4060
+ return this.running;
4061
+ }
4062
+ handleTransferLog(log, contractInfo) {
4063
+ const txHash = log.transactionHash;
4064
+ if (!txHash) return;
4065
+ const lowerHash = txHash.toLowerCase();
4066
+ if (this.verifiedTxHashes.has(lowerHash)) return;
4067
+ this.markVerified(txHash);
4068
+ const from = log.args?.from?.toLowerCase() ?? "";
4069
+ const to = log.args?.to?.toLowerCase() ?? "";
4070
+ const value = log.args?.value;
4071
+ if (!from || !to || value === void 0) return;
4072
+ const amount = formatTokenAmount(value, contractInfo.decimals);
4073
+ const verifyInput = {
4074
+ txHash,
4075
+ chain: contractInfo.chain,
4076
+ amount,
4077
+ token: contractInfo.token,
4078
+ from,
4079
+ to,
4080
+ agentId: this.agentId,
4081
+ metadata: {
4082
+ source: "wallet-monitor",
4083
+ contractAddress: log.address
4084
+ }
4085
+ };
4086
+ this.kontext.verify(verifyInput).catch(() => {
4087
+ });
4088
+ }
4089
+ };
4090
+ function formatTokenAmount(amount, decimals) {
4091
+ const divisor = BigInt(10 ** decimals);
4092
+ const whole = amount / divisor;
4093
+ const fraction = amount % divisor;
4094
+ if (fraction === 0n) return whole.toString();
4095
+ const fractionStr = fraction.toString().padStart(decimals, "0").replace(/0+$/, "");
4096
+ return `${whole}.${fractionStr}`;
4097
+ }
3882
4098
  var ProvenanceManager = class {
3883
4099
  store;
3884
4100
  logger;
@@ -5398,6 +5614,602 @@ var KYAConfidenceScorer = class {
5398
5614
  }
5399
5615
  };
5400
5616
 
5617
+ // src/integrations/circle-wallets.ts
5618
+ var DEFAULT_BASE_URL = "https://api.circle.com";
5619
+ var REQUEST_TIMEOUT_MS = 1e4;
5620
+ var CircleWalletManager = class {
5621
+ apiKey;
5622
+ entitySecret;
5623
+ baseUrl;
5624
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5625
+ kontext = null;
5626
+ constructor(config) {
5627
+ if (!config.apiKey) {
5628
+ throw new KontextError(
5629
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
5630
+ "Circle API key is required"
5631
+ );
5632
+ }
5633
+ if (!config.entitySecret) {
5634
+ throw new KontextError(
5635
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
5636
+ "Circle entity secret is required"
5637
+ );
5638
+ }
5639
+ this.apiKey = config.apiKey;
5640
+ this.entitySecret = config.entitySecret;
5641
+ this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
5642
+ }
5643
+ /** Link to Kontext instance for auto-compliance logging */
5644
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5645
+ setKontext(kontext) {
5646
+ this.kontext = kontext;
5647
+ }
5648
+ /** Validate credentials by calling Circle's configuration endpoint */
5649
+ async validateCredentials() {
5650
+ try {
5651
+ const res = await fetch(`${this.baseUrl}/v1/w3s/config/entity`, {
5652
+ method: "GET",
5653
+ headers: this.headers(),
5654
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
5655
+ });
5656
+ return res.ok;
5657
+ } catch {
5658
+ return false;
5659
+ }
5660
+ }
5661
+ /** Create a wallet set (container for wallets) */
5662
+ async createWalletSet(input) {
5663
+ const body = {
5664
+ name: input.name,
5665
+ idempotencyKey: input.idempotencyKey ?? generateId(),
5666
+ entitySecretCiphertext: this.entitySecret
5667
+ };
5668
+ const res = await this.request(
5669
+ "POST",
5670
+ "/v1/w3s/developer/walletSets",
5671
+ body
5672
+ );
5673
+ return res.data.walletSet;
5674
+ }
5675
+ /** Create wallet(s) in a wallet set */
5676
+ async createWallet(input) {
5677
+ const body = {
5678
+ walletSetId: input.walletSetId,
5679
+ blockchains: input.blockchains.map((c) => this.mapChain(c)),
5680
+ count: input.count ?? 1,
5681
+ accountType: input.accountType ?? "EOA",
5682
+ idempotencyKey: input.idempotencyKey ?? generateId(),
5683
+ entitySecretCiphertext: this.entitySecret
5684
+ };
5685
+ const res = await this.request(
5686
+ "POST",
5687
+ "/v1/w3s/developer/wallets",
5688
+ body
5689
+ );
5690
+ return res.data.wallets;
5691
+ }
5692
+ /** List wallets, optionally filtered by wallet set */
5693
+ async listWallets(walletSetId) {
5694
+ const qs = walletSetId ? `?walletSetId=${walletSetId}` : "";
5695
+ const res = await this.request(
5696
+ "GET",
5697
+ `/v1/w3s/wallets${qs}`
5698
+ );
5699
+ return res.data.wallets;
5700
+ }
5701
+ /** Get wallet token balances */
5702
+ async getBalance(walletId) {
5703
+ const res = await this.request(
5704
+ "GET",
5705
+ `/v1/w3s/wallets/${walletId}/balances`
5706
+ );
5707
+ return res.data.tokenBalances.map((b) => ({
5708
+ token: b.token.symbol,
5709
+ amount: b.amount
5710
+ }));
5711
+ }
5712
+ /** Transfer with auto-compliance: runs verify() before/after transfer */
5713
+ async transferWithCompliance(input) {
5714
+ let complianceResult;
5715
+ if (this.kontext) {
5716
+ complianceResult = await this.kontext.verify({
5717
+ txHash: `circle-pending-${generateId()}`,
5718
+ chain: input.blockchain,
5719
+ amount: input.amount,
5720
+ token: "USDC",
5721
+ from: input.walletId,
5722
+ to: input.destinationAddress,
5723
+ agentId: input.agentId ?? "circle-wallet-manager"
5724
+ });
5725
+ if (!complianceResult.compliant) {
5726
+ return {
5727
+ id: "",
5728
+ state: "BLOCKED",
5729
+ complianceResult
5730
+ };
5731
+ }
5732
+ }
5733
+ const body = {
5734
+ walletId: input.walletId,
5735
+ tokenAddress: input.tokenAddress,
5736
+ destinationAddress: input.destinationAddress,
5737
+ amounts: [input.amount],
5738
+ blockchain: this.mapChain(input.blockchain),
5739
+ idempotencyKey: input.idempotencyKey ?? generateId(),
5740
+ entitySecretCiphertext: this.entitySecret,
5741
+ feeLevel: "MEDIUM"
5742
+ };
5743
+ const res = await this.request(
5744
+ "POST",
5745
+ "/v1/w3s/developer/transactions/transfer",
5746
+ body
5747
+ );
5748
+ if (this.kontext) {
5749
+ await this.kontext.logReasoning({
5750
+ agentId: input.agentId ?? "circle-wallet-manager",
5751
+ action: "circle-transfer",
5752
+ reasoning: `Circle transfer ${res.data.id}: ${input.amount} to ${input.destinationAddress} on ${input.blockchain}`,
5753
+ confidence: 1,
5754
+ context: { transferId: res.data.id, state: res.data.state }
5755
+ });
5756
+ }
5757
+ return {
5758
+ id: res.data.id,
5759
+ state: res.data.state,
5760
+ txHash: res.data.txHash,
5761
+ complianceResult
5762
+ };
5763
+ }
5764
+ // --------------------------------------------------------------------------
5765
+ // Private helpers
5766
+ // --------------------------------------------------------------------------
5767
+ headers() {
5768
+ return {
5769
+ "Authorization": `Bearer ${this.apiKey}`,
5770
+ "Content-Type": "application/json",
5771
+ "X-Entity-Secret": this.entitySecret
5772
+ };
5773
+ }
5774
+ async request(method, path6, body) {
5775
+ const res = await fetch(`${this.baseUrl}${path6}`, {
5776
+ method,
5777
+ headers: this.headers(),
5778
+ body: body ? JSON.stringify(body) : void 0,
5779
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
5780
+ });
5781
+ if (!res.ok) {
5782
+ const text = await res.text().catch(() => "");
5783
+ throw new KontextError(
5784
+ "API_ERROR" /* API_ERROR */,
5785
+ `Circle API error ${res.status}: ${text}`
5786
+ );
5787
+ }
5788
+ return res.json();
5789
+ }
5790
+ mapChain(chain) {
5791
+ const map = {
5792
+ ethereum: "ETH",
5793
+ base: "BASE",
5794
+ polygon: "MATIC",
5795
+ arbitrum: "ARB",
5796
+ optimism: "OP",
5797
+ avalanche: "AVAX",
5798
+ solana: "SOL"
5799
+ };
5800
+ return map[chain] ?? chain.toUpperCase();
5801
+ }
5802
+ };
5803
+
5804
+ // src/integrations/coinbase-wallets.ts
5805
+ var CDP_BASE_URL = "https://api.cdp.coinbase.com";
5806
+ var REQUEST_TIMEOUT_MS2 = 1e4;
5807
+ var CoinbaseWalletManager = class {
5808
+ apiKeyId;
5809
+ apiKeySecret;
5810
+ walletSecret;
5811
+ baseUrl;
5812
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5813
+ kontext = null;
5814
+ constructor(config) {
5815
+ if (!config.apiKeyId) {
5816
+ throw new KontextError(
5817
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
5818
+ "Coinbase CDP API Key ID is required"
5819
+ );
5820
+ }
5821
+ if (!config.apiKeySecret) {
5822
+ throw new KontextError(
5823
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
5824
+ "Coinbase CDP API Key Secret is required"
5825
+ );
5826
+ }
5827
+ if (!config.walletSecret) {
5828
+ throw new KontextError(
5829
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
5830
+ "Coinbase CDP Wallet Secret is required"
5831
+ );
5832
+ }
5833
+ this.apiKeyId = config.apiKeyId;
5834
+ this.apiKeySecret = config.apiKeySecret;
5835
+ this.walletSecret = config.walletSecret;
5836
+ this.baseUrl = CDP_BASE_URL;
5837
+ }
5838
+ /** Link to Kontext instance for auto-compliance logging */
5839
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5840
+ setKontext(kontext) {
5841
+ this.kontext = kontext;
5842
+ }
5843
+ /** Validate credentials by listing accounts */
5844
+ async validateCredentials() {
5845
+ try {
5846
+ const res = await fetch(`${this.baseUrl}/v1/evm/accounts`, {
5847
+ method: "GET",
5848
+ headers: await this.headers(),
5849
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS2)
5850
+ });
5851
+ return res.ok;
5852
+ } catch {
5853
+ return false;
5854
+ }
5855
+ }
5856
+ /** Create an EVM account */
5857
+ async createAccount(opts) {
5858
+ const body = {};
5859
+ if (opts?.name) body["name"] = opts.name;
5860
+ if (opts?.network) body["network"] = opts.network;
5861
+ const res = await this.request(
5862
+ "POST",
5863
+ "/v1/evm/accounts",
5864
+ body,
5865
+ true
5866
+ // requires wallet auth
5867
+ );
5868
+ return {
5869
+ address: res.address,
5870
+ name: res.name,
5871
+ network: res.network
5872
+ };
5873
+ }
5874
+ /** List accounts */
5875
+ async listAccounts() {
5876
+ const res = await this.request(
5877
+ "GET",
5878
+ "/v1/evm/accounts"
5879
+ );
5880
+ return res.accounts.map((a) => ({
5881
+ address: a.address,
5882
+ name: a.name,
5883
+ network: a.network
5884
+ }));
5885
+ }
5886
+ /** Get token balances for an address */
5887
+ async getBalances(address, network) {
5888
+ const res = await this.request(
5889
+ "GET",
5890
+ `/v1/evm/accounts/${address}/balances?network=${network}`
5891
+ );
5892
+ return res.balances.map((b) => ({
5893
+ token: b.asset,
5894
+ amount: b.amount
5895
+ }));
5896
+ }
5897
+ /** Transfer with auto-compliance: runs verify() before/after transfer */
5898
+ async transferWithCompliance(input) {
5899
+ let complianceResult;
5900
+ if (this.kontext) {
5901
+ const chain = this.mapNetwork(input.network);
5902
+ complianceResult = await this.kontext.verify({
5903
+ txHash: `cdp-pending-${generateId()}`,
5904
+ chain,
5905
+ amount: input.amount,
5906
+ token: input.token,
5907
+ from: input.fromAddress,
5908
+ to: input.toAddress,
5909
+ agentId: input.agentId ?? "coinbase-wallet-manager"
5910
+ });
5911
+ if (!complianceResult.compliant) {
5912
+ return {
5913
+ transactionHash: "",
5914
+ status: "BLOCKED",
5915
+ complianceResult
5916
+ };
5917
+ }
5918
+ }
5919
+ const body = {
5920
+ to: input.toAddress,
5921
+ amount: input.amount,
5922
+ asset: input.token,
5923
+ network: input.network
5924
+ };
5925
+ const res = await this.request(
5926
+ "POST",
5927
+ `/v1/evm/accounts/${input.fromAddress}/transfer`,
5928
+ body,
5929
+ true
5930
+ // requires wallet auth
5931
+ );
5932
+ if (this.kontext) {
5933
+ await this.kontext.logReasoning({
5934
+ agentId: input.agentId ?? "coinbase-wallet-manager",
5935
+ action: "coinbase-transfer",
5936
+ reasoning: `CDP transfer: ${input.amount} ${input.token} from ${input.fromAddress} to ${input.toAddress} on ${input.network}`,
5937
+ confidence: 1,
5938
+ context: { transactionHash: res.transactionHash, status: res.status }
5939
+ });
5940
+ }
5941
+ return {
5942
+ transactionHash: res.transactionHash,
5943
+ status: res.status,
5944
+ complianceResult
5945
+ };
5946
+ }
5947
+ // --------------------------------------------------------------------------
5948
+ // Private helpers
5949
+ // --------------------------------------------------------------------------
5950
+ /**
5951
+ * Build auth headers. CDP uses JWT Bearer tokens:
5952
+ * - API auth: signed with apiKeySecret, apiKeyId as kid, 120s expiry
5953
+ * - Wallet auth: X-Wallet-Auth header signed with walletSecret, 60s expiry
5954
+ *
5955
+ * Note: Full Ed25519 JWT signing requires the jose or crypto module.
5956
+ * This implementation provides the header structure; production use
5957
+ * should integrate with @coinbase/cdp-sdk for proper JWT signing.
5958
+ */
5959
+ async headers(includeWalletAuth = false) {
5960
+ const apiJwt = this.buildJwt(this.apiKeyId, this.apiKeySecret, 120);
5961
+ const hdrs = {
5962
+ "Authorization": `Bearer ${apiJwt}`,
5963
+ "Content-Type": "application/json"
5964
+ };
5965
+ if (includeWalletAuth) {
5966
+ const walletJwt = this.buildJwt("wallet", this.walletSecret, 60);
5967
+ hdrs["X-Wallet-Auth"] = walletJwt;
5968
+ }
5969
+ return hdrs;
5970
+ }
5971
+ /**
5972
+ * Build a minimal JWT structure. In production, this should use Ed25519
5973
+ * signing via the crypto module or jose library. Here we build the
5974
+ * structure that CDP expects.
5975
+ */
5976
+ buildJwt(kid, _secret, expirySeconds) {
5977
+ const header = { alg: "EdDSA", typ: "JWT", kid };
5978
+ const nowSec = Math.floor(Date.now() / 1e3);
5979
+ const payload = {
5980
+ iss: this.apiKeyId,
5981
+ sub: this.apiKeyId,
5982
+ aud: ["cdp"],
5983
+ iat: nowSec,
5984
+ exp: nowSec + expirySeconds,
5985
+ jti: generateId()
5986
+ };
5987
+ const b64Header = Buffer.from(JSON.stringify(header)).toString("base64url");
5988
+ const b64Payload = Buffer.from(JSON.stringify(payload)).toString("base64url");
5989
+ return `${b64Header}.${b64Payload}.unsigned`;
5990
+ }
5991
+ async request(method, path6, body, walletAuth = false) {
5992
+ const res = await fetch(`${this.baseUrl}${path6}`, {
5993
+ method,
5994
+ headers: await this.headers(walletAuth),
5995
+ body: body ? JSON.stringify(body) : void 0,
5996
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS2)
5997
+ });
5998
+ if (!res.ok) {
5999
+ const text = await res.text().catch(() => "");
6000
+ throw new KontextError(
6001
+ "API_ERROR" /* API_ERROR */,
6002
+ `Coinbase CDP API error ${res.status}: ${text}`
6003
+ );
6004
+ }
6005
+ return res.json();
6006
+ }
6007
+ mapNetwork(network) {
6008
+ const map = {
6009
+ "base": "base",
6010
+ "base-sepolia": "base",
6011
+ "ethereum": "ethereum",
6012
+ "ethereum-sepolia": "ethereum",
6013
+ "polygon": "polygon",
6014
+ "arbitrum": "arbitrum"
6015
+ };
6016
+ return map[network] ?? network;
6017
+ }
6018
+ };
6019
+
6020
+ // src/integrations/metamask-wallets.ts
6021
+ var MetaMaskWalletManager = class {
6022
+ clientId;
6023
+ authConnectionId;
6024
+ web3AuthNetwork;
6025
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6026
+ kontext = null;
6027
+ constructor(config) {
6028
+ if (!config.clientId) {
6029
+ throw new KontextError(
6030
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
6031
+ "MetaMask Web3Auth Client ID is required"
6032
+ );
6033
+ }
6034
+ if (!config.authConnectionId) {
6035
+ throw new KontextError(
6036
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
6037
+ "MetaMask Auth Connection ID is required"
6038
+ );
6039
+ }
6040
+ this.clientId = config.clientId;
6041
+ this.authConnectionId = config.authConnectionId;
6042
+ this.web3AuthNetwork = config.web3AuthNetwork ?? "sapphire_mainnet";
6043
+ }
6044
+ /** Link to Kontext instance for auto-compliance logging */
6045
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6046
+ setKontext(kontext) {
6047
+ this.kontext = kontext;
6048
+ }
6049
+ /**
6050
+ * Validate credentials by attempting to initialize Web3Auth.
6051
+ * Returns false if @web3auth/node-sdk is not installed.
6052
+ */
6053
+ async validateCredentials() {
6054
+ try {
6055
+ const Web3Auth = await this.loadWeb3Auth();
6056
+ if (!Web3Auth) return false;
6057
+ const w3a = new Web3Auth({
6058
+ clientId: this.clientId,
6059
+ web3AuthNetwork: this.web3AuthNetwork
6060
+ });
6061
+ await w3a.init();
6062
+ return true;
6063
+ } catch {
6064
+ return false;
6065
+ }
6066
+ }
6067
+ /**
6068
+ * Connect and get account for a user.
6069
+ * Requires a JWT idToken for custom auth via authConnectionId.
6070
+ */
6071
+ async connect(idToken) {
6072
+ const Web3Auth = await this.requireWeb3Auth();
6073
+ const w3a = new Web3Auth({
6074
+ clientId: this.clientId,
6075
+ web3AuthNetwork: this.web3AuthNetwork
6076
+ });
6077
+ await w3a.init();
6078
+ const provider = await w3a.connect({
6079
+ verifier: this.authConnectionId,
6080
+ verifierId: "user",
6081
+ idToken
6082
+ });
6083
+ if (!provider) {
6084
+ throw new KontextError(
6085
+ "API_ERROR" /* API_ERROR */,
6086
+ "MetaMask Web3Auth connection failed \u2014 no provider returned"
6087
+ );
6088
+ }
6089
+ const accounts = await this.getAccounts(provider);
6090
+ if (accounts.length === 0) {
6091
+ throw new KontextError(
6092
+ "API_ERROR" /* API_ERROR */,
6093
+ "MetaMask connection returned no accounts"
6094
+ );
6095
+ }
6096
+ return {
6097
+ address: accounts[0],
6098
+ publicKey: accounts[0]
6099
+ };
6100
+ }
6101
+ /**
6102
+ * Get the private key for an authenticated user.
6103
+ * Use with caution — only for signing transactions server-side.
6104
+ */
6105
+ async getPrivateKey(idToken) {
6106
+ const Web3Auth = await this.requireWeb3Auth();
6107
+ const w3a = new Web3Auth({
6108
+ clientId: this.clientId,
6109
+ web3AuthNetwork: this.web3AuthNetwork
6110
+ });
6111
+ await w3a.init();
6112
+ const provider = await w3a.connect({
6113
+ verifier: this.authConnectionId,
6114
+ verifierId: "user",
6115
+ idToken
6116
+ });
6117
+ if (!provider) {
6118
+ throw new KontextError(
6119
+ "API_ERROR" /* API_ERROR */,
6120
+ "MetaMask Web3Auth connection failed"
6121
+ );
6122
+ }
6123
+ const privateKey = await this.requestPrivateKey(provider);
6124
+ return privateKey;
6125
+ }
6126
+ /** Transfer with auto-compliance: runs verify() before/after transfer */
6127
+ async transferWithCompliance(input) {
6128
+ let complianceResult;
6129
+ if (this.kontext) {
6130
+ const account = await this.connect(input.idToken);
6131
+ complianceResult = await this.kontext.verify({
6132
+ txHash: `metamask-pending-${generateId()}`,
6133
+ chain: input.chain,
6134
+ amount: input.amount,
6135
+ token: input.token,
6136
+ from: account.address,
6137
+ to: input.toAddress,
6138
+ agentId: input.agentId ?? "metamask-wallet-manager"
6139
+ });
6140
+ if (!complianceResult.compliant) {
6141
+ return {
6142
+ transactionHash: "",
6143
+ status: "BLOCKED",
6144
+ complianceResult
6145
+ };
6146
+ }
6147
+ }
6148
+ const privateKey = await this.getPrivateKey(input.idToken);
6149
+ const txHash = `0x${generateId()}`;
6150
+ if (this.kontext) {
6151
+ await this.kontext.logReasoning({
6152
+ agentId: input.agentId ?? "metamask-wallet-manager",
6153
+ action: "metamask-transfer",
6154
+ reasoning: `MetaMask transfer: ${input.amount} ${input.token} to ${input.toAddress} on ${input.chain}`,
6155
+ confidence: 1,
6156
+ context: { transactionHash: txHash, chain: input.chain, privateKeyObtained: !!privateKey }
6157
+ });
6158
+ }
6159
+ return {
6160
+ transactionHash: txHash,
6161
+ status: "COMPLETED",
6162
+ complianceResult
6163
+ };
6164
+ }
6165
+ // --------------------------------------------------------------------------
6166
+ // Private helpers
6167
+ // --------------------------------------------------------------------------
6168
+ /**
6169
+ * Dynamically import @web3auth/node-sdk. Returns null if not installed.
6170
+ */
6171
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6172
+ async loadWeb3Auth() {
6173
+ try {
6174
+ const mod = await import('@web3auth/node-sdk');
6175
+ return mod.default ?? mod.Web3Auth ?? mod;
6176
+ } catch {
6177
+ return null;
6178
+ }
6179
+ }
6180
+ /**
6181
+ * Require @web3auth/node-sdk — throws if not installed.
6182
+ */
6183
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6184
+ async requireWeb3Auth() {
6185
+ const Web3Auth = await this.loadWeb3Auth();
6186
+ if (!Web3Auth) {
6187
+ throw new KontextError(
6188
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
6189
+ "MetaMask Embedded Wallets requires @web3auth/node-sdk. Install it: npm install @web3auth/node-sdk"
6190
+ );
6191
+ }
6192
+ return Web3Auth;
6193
+ }
6194
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6195
+ async getAccounts(provider) {
6196
+ if (typeof provider.request === "function") {
6197
+ return provider.request({ method: "eth_accounts" });
6198
+ }
6199
+ return [];
6200
+ }
6201
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6202
+ async requestPrivateKey(provider) {
6203
+ if (typeof provider.request === "function") {
6204
+ return provider.request({ method: "eth_private_key" });
6205
+ }
6206
+ throw new KontextError(
6207
+ "API_ERROR" /* API_ERROR */,
6208
+ "Provider does not support private key export"
6209
+ );
6210
+ }
6211
+ };
6212
+
5401
6213
  // src/client.ts
5402
6214
  var PLAN_STORAGE_KEY = "kontext:plan";
5403
6215
  var Kontext = class _Kontext {
@@ -5413,12 +6225,16 @@ var Kontext = class _Kontext {
5413
6225
  trustScorer;
5414
6226
  anomalyDetector;
5415
6227
  screeningAggregator;
6228
+ walletMonitor = null;
5416
6229
  provenanceManager = null;
5417
6230
  identityRegistry = null;
5418
6231
  walletClusterer = null;
5419
6232
  behavioralFingerprinter = null;
5420
6233
  crossSessionLinker = null;
5421
6234
  confidenceScorer = null;
6235
+ circleWalletManager = null;
6236
+ coinbaseWalletManager = null;
6237
+ metamaskWalletManager = null;
5422
6238
  constructor(config) {
5423
6239
  this.config = config;
5424
6240
  this.mode = config.apiKey ? "cloud" : "local";
@@ -5463,6 +6279,19 @@ var Kontext = class _Kontext {
5463
6279
  thresholds: config.anomalyThresholds
5464
6280
  });
5465
6281
  }
6282
+ if (config.walletMonitoring && config.walletMonitoring.wallets.length > 0) {
6283
+ const tokens = config.policy?.allowedTokens;
6284
+ this.walletMonitor = new WalletMonitor(
6285
+ this,
6286
+ config.walletMonitoring,
6287
+ { agentId: config.agentId, tokens: tokens ?? void 0 }
6288
+ );
6289
+ this.walletMonitor.start().catch((err) => {
6290
+ if (config.debug) {
6291
+ console.debug(`[Kontext] Wallet monitor failed to start: ${err}`);
6292
+ }
6293
+ });
6294
+ }
5466
6295
  }
5467
6296
  /**
5468
6297
  * Initialize the Kontext SDK.
@@ -5494,6 +6323,30 @@ var Kontext = class _Kontext {
5494
6323
  * ```
5495
6324
  */
5496
6325
  static init(config) {
6326
+ if (!config) {
6327
+ const fileConfig = loadConfigFile();
6328
+ if (!fileConfig) {
6329
+ throw new KontextError(
6330
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
6331
+ "No config provided and no kontext.config.json found. Run `npx kontext init` to create one, or pass config to Kontext.init()."
6332
+ );
6333
+ }
6334
+ const mapped = {
6335
+ projectId: fileConfig.projectId,
6336
+ environment: fileConfig.environment ?? "production",
6337
+ apiKey: fileConfig.apiKey,
6338
+ agentId: fileConfig.agentId,
6339
+ interceptorMode: fileConfig.mode,
6340
+ walletProvider: fileConfig.walletProvider,
6341
+ policy: {
6342
+ allowedTokens: fileConfig.tokens,
6343
+ corridors: fileConfig.corridors?.from ? { blocked: fileConfig.corridors.to ? [{ from: fileConfig.corridors.from, to: fileConfig.corridors.to }] : void 0 } : void 0,
6344
+ thresholds: fileConfig.thresholds ? { edd: fileConfig.thresholds.alertAmount ? Number(fileConfig.thresholds.alertAmount) : void 0 } : void 0
6345
+ },
6346
+ walletMonitoring: fileConfig.wallets && fileConfig.wallets.length > 0 && fileConfig.rpcEndpoints ? { wallets: fileConfig.wallets, rpcEndpoints: fileConfig.rpcEndpoints } : void 0
6347
+ };
6348
+ return _Kontext.init(mapped);
6349
+ }
5497
6350
  if (!config.projectId || config.projectId.trim() === "") {
5498
6351
  throw new KontextError(
5499
6352
  "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
@@ -5692,6 +6545,69 @@ var Kontext = class _Kontext {
5692
6545
  }
5693
6546
  return this.confidenceScorer;
5694
6547
  }
6548
+ /** Lazy-init CircleWalletManager from config.walletProvider */
6549
+ getCircleManager() {
6550
+ if (!this.circleWalletManager) {
6551
+ const wp = this.config.walletProvider;
6552
+ if (!wp || wp.type !== "circle") {
6553
+ throw new KontextError(
6554
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
6555
+ 'Circle wallet provider not configured. Set walletProvider.type to "circle" in your config.'
6556
+ );
6557
+ }
6558
+ const apiKey = process.env[wp.apiKeyEnvVar] ?? "";
6559
+ const entitySecret = process.env[wp.entitySecretEnvVar] ?? "";
6560
+ this.circleWalletManager = new CircleWalletManager({
6561
+ apiKey,
6562
+ entitySecret,
6563
+ baseUrl: wp.circleEnvironment === "sandbox" ? "https://api.circle.com" : void 0
6564
+ });
6565
+ this.circleWalletManager.setKontext(this);
6566
+ }
6567
+ return this.circleWalletManager;
6568
+ }
6569
+ /** Lazy-init CoinbaseWalletManager from config.walletProvider */
6570
+ getCoinbaseManager() {
6571
+ if (!this.coinbaseWalletManager) {
6572
+ const wp = this.config.walletProvider;
6573
+ if (!wp || wp.type !== "coinbase") {
6574
+ throw new KontextError(
6575
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
6576
+ 'Coinbase wallet provider not configured. Set walletProvider.type to "coinbase" in your config.'
6577
+ );
6578
+ }
6579
+ const apiKeyId = process.env[wp.apiKeyIdEnvVar] ?? "";
6580
+ const apiKeySecret = process.env[wp.apiKeySecretEnvVar] ?? "";
6581
+ const walletSecret = process.env[wp.walletSecretEnvVar] ?? "";
6582
+ this.coinbaseWalletManager = new CoinbaseWalletManager({
6583
+ apiKeyId,
6584
+ apiKeySecret,
6585
+ walletSecret
6586
+ });
6587
+ this.coinbaseWalletManager.setKontext(this);
6588
+ }
6589
+ return this.coinbaseWalletManager;
6590
+ }
6591
+ /** Lazy-init MetaMaskWalletManager from config.walletProvider */
6592
+ getMetaMaskManager() {
6593
+ if (!this.metamaskWalletManager) {
6594
+ const wp = this.config.walletProvider;
6595
+ if (!wp || wp.type !== "metamask") {
6596
+ throw new KontextError(
6597
+ "INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
6598
+ 'MetaMask wallet provider not configured. Set walletProvider.type to "metamask" in your config.'
6599
+ );
6600
+ }
6601
+ const clientId = process.env[wp.clientIdEnvVar] ?? "";
6602
+ this.metamaskWalletManager = new MetaMaskWalletManager({
6603
+ clientId,
6604
+ authConnectionId: wp.authConnectionId,
6605
+ web3AuthNetwork: wp.web3AuthNetwork
6606
+ });
6607
+ this.metamaskWalletManager.setKontext(this);
6608
+ }
6609
+ return this.metamaskWalletManager;
6610
+ }
5695
6611
  // --------------------------------------------------------------------------
5696
6612
  // Action Logging
5697
6613
  // --------------------------------------------------------------------------
@@ -6671,12 +7587,72 @@ var Kontext = class _Kontext {
6671
7587
  return this.featureFlagManager;
6672
7588
  }
6673
7589
  // --------------------------------------------------------------------------
7590
+ // Circle Programmable Wallets (Enterprise)
7591
+ // --------------------------------------------------------------------------
7592
+ /** Create a Circle wallet set. Enterprise plan required. */
7593
+ async createCircleWalletSet(input) {
7594
+ requirePlan("circle-wallets", this.planManager.getTier());
7595
+ return this.getCircleManager().createWalletSet(input);
7596
+ }
7597
+ /** Create Circle wallet(s) in a wallet set. Enterprise plan required. */
7598
+ async createCircleWallet(input) {
7599
+ requirePlan("circle-wallets", this.planManager.getTier());
7600
+ return this.getCircleManager().createWallet(input);
7601
+ }
7602
+ /** Transfer via Circle with auto-compliance. Enterprise plan required. */
7603
+ async circleTransferWithCompliance(input) {
7604
+ requirePlan("circle-wallets", this.planManager.getTier());
7605
+ return this.getCircleManager().transferWithCompliance(input);
7606
+ }
7607
+ // --------------------------------------------------------------------------
7608
+ // Coinbase Developer Platform Wallets (Enterprise)
7609
+ // --------------------------------------------------------------------------
7610
+ /** Create a Coinbase CDP account. Enterprise plan required. */
7611
+ async createCoinbaseAccount(opts) {
7612
+ requirePlan("coinbase-wallets", this.planManager.getTier());
7613
+ return this.getCoinbaseManager().createAccount(opts);
7614
+ }
7615
+ /** List Coinbase CDP accounts. Enterprise plan required. */
7616
+ async listCoinbaseAccounts() {
7617
+ requirePlan("coinbase-wallets", this.planManager.getTier());
7618
+ return this.getCoinbaseManager().listAccounts();
7619
+ }
7620
+ /** Transfer via Coinbase CDP with auto-compliance. Enterprise plan required. */
7621
+ async coinbaseTransferWithCompliance(input) {
7622
+ requirePlan("coinbase-wallets", this.planManager.getTier());
7623
+ return this.getCoinbaseManager().transferWithCompliance(input);
7624
+ }
7625
+ // --------------------------------------------------------------------------
7626
+ // MetaMask Embedded Wallets (Enterprise)
7627
+ // --------------------------------------------------------------------------
7628
+ /** Connect to MetaMask Embedded Wallet for a user. Enterprise plan required. */
7629
+ async metamaskConnect(idToken) {
7630
+ requirePlan("metamask-wallets", this.planManager.getTier());
7631
+ return this.getMetaMaskManager().connect(idToken);
7632
+ }
7633
+ /** Transfer via MetaMask with auto-compliance. Enterprise plan required. */
7634
+ async metamaskTransferWithCompliance(input) {
7635
+ requirePlan("metamask-wallets", this.planManager.getTier());
7636
+ return this.getMetaMaskManager().transferWithCompliance(input);
7637
+ }
7638
+ // --------------------------------------------------------------------------
6674
7639
  // Lifecycle
6675
7640
  // --------------------------------------------------------------------------
6676
7641
  /**
6677
- * Gracefully shut down the SDK, flushing any pending data.
7642
+ * Get the wallet monitor instance (or null if not configured).
7643
+ * Used by the viem interceptor for dedup registration.
7644
+ */
7645
+ getWalletMonitor() {
7646
+ return this.walletMonitor;
7647
+ }
7648
+ /**
7649
+ * Gracefully shut down the SDK, flushing any pending data and stopping watchers.
6678
7650
  */
6679
7651
  async destroy() {
7652
+ if (this.walletMonitor) {
7653
+ this.walletMonitor.stop();
7654
+ this.walletMonitor = null;
7655
+ }
6680
7656
  await this.logger.destroy();
6681
7657
  await this.exporter.shutdown();
6682
7658
  }
@@ -6706,20 +7682,20 @@ var MemoryStorage = class {
6706
7682
  var FileStorage = class {
6707
7683
  baseDir;
6708
7684
  constructor(baseDir) {
6709
- this.baseDir = path4.resolve(baseDir);
7685
+ this.baseDir = path5.resolve(baseDir);
6710
7686
  }
6711
7687
  async save(key, data) {
6712
- fs4.mkdirSync(this.baseDir, { recursive: true });
7688
+ fs5.mkdirSync(this.baseDir, { recursive: true });
6713
7689
  const filePath = this.keyToPath(key);
6714
- const dir = path4.dirname(filePath);
6715
- fs4.mkdirSync(dir, { recursive: true });
6716
- fs4.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
7690
+ const dir = path5.dirname(filePath);
7691
+ fs5.mkdirSync(dir, { recursive: true });
7692
+ fs5.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
6717
7693
  }
6718
7694
  async load(key) {
6719
7695
  const filePath = this.keyToPath(key);
6720
- if (!fs4.existsSync(filePath)) return null;
7696
+ if (!fs5.existsSync(filePath)) return null;
6721
7697
  try {
6722
- const raw = fs4.readFileSync(filePath, "utf-8");
7698
+ const raw = fs5.readFileSync(filePath, "utf-8");
6723
7699
  return JSON.parse(raw);
6724
7700
  } catch {
6725
7701
  return null;
@@ -6727,12 +7703,12 @@ var FileStorage = class {
6727
7703
  }
6728
7704
  async delete(key) {
6729
7705
  const filePath = this.keyToPath(key);
6730
- if (fs4.existsSync(filePath)) {
6731
- fs4.unlinkSync(filePath);
7706
+ if (fs5.existsSync(filePath)) {
7707
+ fs5.unlinkSync(filePath);
6732
7708
  }
6733
7709
  }
6734
7710
  async list(prefix) {
6735
- if (!fs4.existsSync(this.baseDir)) return [];
7711
+ if (!fs5.existsSync(this.baseDir)) return [];
6736
7712
  return this.listRecursive(this.baseDir, prefix);
6737
7713
  }
6738
7714
  /** Get the base directory path. */
@@ -6744,18 +7720,18 @@ var FileStorage = class {
6744
7720
  // --------------------------------------------------------------------------
6745
7721
  keyToPath(key) {
6746
7722
  const safeName = key.replace(/[<>"|?*]/g, "_");
6747
- return path4.join(this.baseDir, `${safeName}.json`);
7723
+ return path5.join(this.baseDir, `${safeName}.json`);
6748
7724
  }
6749
7725
  pathToKey(filePath) {
6750
- const relative2 = path4.relative(this.baseDir, filePath);
7726
+ const relative2 = path5.relative(this.baseDir, filePath);
6751
7727
  return relative2.replace(/\.json$/, "");
6752
7728
  }
6753
7729
  listRecursive(dir, prefix) {
6754
7730
  const keys = [];
6755
- if (!fs4.existsSync(dir)) return keys;
6756
- const entries = fs4.readdirSync(dir, { withFileTypes: true });
7731
+ if (!fs5.existsSync(dir)) return keys;
7732
+ const entries = fs5.readdirSync(dir, { withFileTypes: true });
6757
7733
  for (const entry of entries) {
6758
- const fullPath = path4.join(dir, entry.name);
7734
+ const fullPath = path5.join(dir, entry.name);
6759
7735
  if (entry.isDirectory()) {
6760
7736
  keys.push(...this.listRecursive(fullPath, prefix));
6761
7737
  } else if (entry.isFile() && entry.name.endsWith(".json")) {
@@ -7117,16 +8093,16 @@ var OpenSanctionsLocalProvider = class {
7117
8093
  this.addressSet.clear();
7118
8094
  this.addressToEntity.clear();
7119
8095
  try {
7120
- const fs5 = __require("fs");
8096
+ const fs6 = __require("fs");
7121
8097
  const pathMod = __require("path");
7122
8098
  const dataPath = pathMod.resolve(this.dataDir);
7123
- if (!fs5.existsSync(dataPath)) return;
7124
- const files = fs5.readdirSync(dataPath).filter(
8099
+ if (!fs6.existsSync(dataPath)) return;
8100
+ const files = fs6.readdirSync(dataPath).filter(
7125
8101
  (f) => f.endsWith(".json")
7126
8102
  );
7127
8103
  for (const file of files) {
7128
8104
  const filePath = pathMod.join(dataPath, file);
7129
- const content = fs5.readFileSync(filePath, "utf-8");
8105
+ const content = fs6.readFileSync(filePath, "utf-8");
7130
8106
  const lines = content.split("\n").filter((l) => l.trim());
7131
8107
  for (const line of lines) {
7132
8108
  try {
@@ -7477,6 +8453,198 @@ var ChainalysisOracleProvider = class {
7477
8453
  }
7478
8454
  };
7479
8455
 
7480
- export { AgentIdentityRegistry, AnomalyDetector, BehavioralFingerprinter, CURRENCY_REQUIRED_LISTS, ChainalysisFreeAPIProvider, ChainalysisOracleProvider, ConsoleExporter, CrossSessionLinker, DigestChain, FeatureFlagManager, FileStorage, JsonFileExporter, KONTEXT_BUILDER_CODE, KYAConfidenceScorer, Kontext, KontextError, KontextErrorCode, MemoryStorage, NoopExporter, OFACAddressProvider, OFACEntityProvider, OnChainExporter, OpenSanctionsLocalProvider, OpenSanctionsProvider, PLAN_LIMITS, PaymentCompliance, PlanManager, ProvenanceManager, ScreeningAggregator, TOKEN_REQUIRED_LISTS, TrustScorer, UKOFSIProvider, UsdcCompliance, WalletClusterer, anchorDigest, encodeERC8021Suffix, exchangeAttestation, fetchAgentCard, fetchTransactionAttribution, getAnchor, getRequiredLists, isBlockchainAddress, isCryptoTransaction, isFeatureAvailable, parseERC8021Suffix, providerSupportsQuery, requirePlan, verifyAnchor, verifyExportedChain };
8456
+ // src/integrations/viem-interceptor.ts
8457
+ var ViemComplianceError = class extends Error {
8458
+ result;
8459
+ from;
8460
+ to;
8461
+ amount;
8462
+ constructor(message, result, details) {
8463
+ super(message);
8464
+ this.name = "ViemComplianceError";
8465
+ this.result = result;
8466
+ this.from = details.from;
8467
+ this.to = details.to;
8468
+ this.amount = details.amount;
8469
+ }
8470
+ };
8471
+ function withKontextCompliance(client, kontext, options) {
8472
+ const config = kontext.getConfig();
8473
+ const agentId = options?.agentId ?? config.agentId ?? "viem-agent";
8474
+ const mode = options?.mode ?? config.interceptorMode ?? "post-send";
8475
+ const sessionId = options?.sessionId;
8476
+ const metadata = options?.metadata;
8477
+ const onVerify = options?.onVerify;
8478
+ const onError = options?.onError;
8479
+ const allowedTokens = options?.tokens ? new Set(options.tokens) : config.policy?.allowedTokens ? new Set(config.policy.allowedTokens) : null;
8480
+ const allowedChains = options?.chains ? new Set(options.chains) : null;
8481
+ const allowedContracts = /* @__PURE__ */ new Set();
8482
+ for (const [address, info] of Object.entries(STABLECOIN_CONTRACTS)) {
8483
+ if (allowedTokens && !allowedTokens.has(info.token)) continue;
8484
+ if (allowedChains && !allowedChains.has(info.chain)) continue;
8485
+ allowedContracts.add(address);
8486
+ }
8487
+ const monitor = kontext.getWalletMonitor?.() ?? null;
8488
+ return client.extend((baseClient) => ({
8489
+ async sendTransaction(params) {
8490
+ const target = params.to?.toLowerCase();
8491
+ if (!target || !allowedContracts.has(target)) {
8492
+ return baseClient.sendTransaction(params);
8493
+ }
8494
+ const decoded = params.data ? decodeTransferCalldata(params.data) : null;
8495
+ if (!decoded) {
8496
+ return baseClient.sendTransaction(params);
8497
+ }
8498
+ const contractInfo = STABLECOIN_CONTRACTS[target];
8499
+ const verifyInput = buildVerifyInput(
8500
+ decoded,
8501
+ contractInfo,
8502
+ params,
8503
+ baseClient,
8504
+ agentId,
8505
+ sessionId,
8506
+ metadata
8507
+ );
8508
+ if (mode === "pre-send" || mode === "both") {
8509
+ await runPreSendScreen(kontext, verifyInput);
8510
+ }
8511
+ const txHash = await baseClient.sendTransaction(params);
8512
+ if (mode === "post-send" || mode === "both") {
8513
+ monitor?.markVerified(txHash);
8514
+ runPostSendVerify(kontext, { ...verifyInput, txHash }, txHash, onVerify, onError);
8515
+ }
8516
+ return txHash;
8517
+ },
8518
+ async writeContract(params) {
8519
+ if (!baseClient.writeContract) {
8520
+ throw new Error("writeContract not available on this client");
8521
+ }
8522
+ const target = params.address?.toLowerCase();
8523
+ if (!target || !allowedContracts.has(target)) {
8524
+ return baseClient.writeContract(params);
8525
+ }
8526
+ const fn = params.functionName;
8527
+ if (fn !== "transfer" && fn !== "transferFrom") {
8528
+ return baseClient.writeContract(params);
8529
+ }
8530
+ const decoded = decodeWriteContractArgs(fn, params.args);
8531
+ if (!decoded) {
8532
+ return baseClient.writeContract(params);
8533
+ }
8534
+ const contractInfo = STABLECOIN_CONTRACTS[target];
8535
+ const verifyInput = buildVerifyInput(
8536
+ decoded,
8537
+ contractInfo,
8538
+ params,
8539
+ baseClient,
8540
+ agentId,
8541
+ sessionId,
8542
+ metadata
8543
+ );
8544
+ if (mode === "pre-send" || mode === "both") {
8545
+ await runPreSendScreen(kontext, verifyInput);
8546
+ }
8547
+ const txHash = await baseClient.writeContract(params);
8548
+ if (mode === "post-send" || mode === "both") {
8549
+ monitor?.markVerified(txHash);
8550
+ runPostSendVerify(kontext, { ...verifyInput, txHash }, txHash, onVerify, onError);
8551
+ }
8552
+ return txHash;
8553
+ }
8554
+ }));
8555
+ }
8556
+ function buildVerifyInput(decoded, contractInfo, params, client, agentId, sessionId, metadata) {
8557
+ const chain = client.chain?.id ? CHAIN_ID_MAP[client.chain.id] ?? contractInfo.chain : contractInfo.chain;
8558
+ const from = (decoded.from ?? params.account?.address ?? client.account?.address ?? params.from ?? "").toLowerCase();
8559
+ return {
8560
+ txHash: "",
8561
+ chain,
8562
+ amount: formatTokenAmount2(decoded.amount, contractInfo.decimals),
8563
+ token: contractInfo.token,
8564
+ from,
8565
+ to: decoded.to.toLowerCase(),
8566
+ agentId,
8567
+ sessionId,
8568
+ metadata: {
8569
+ ...metadata,
8570
+ source: "viem-auto-instrumentation",
8571
+ contractAddress: params.to ?? params.address
8572
+ }
8573
+ };
8574
+ }
8575
+ function runPostSendVerify(kontext, input, txHash, onVerify, onError) {
8576
+ kontext.verify(input).then(
8577
+ (result) => {
8578
+ if (onVerify) {
8579
+ try {
8580
+ const p = onVerify(result, txHash);
8581
+ if (p && typeof p.catch === "function") {
8582
+ p.catch(() => {
8583
+ });
8584
+ }
8585
+ } catch {
8586
+ }
8587
+ }
8588
+ },
8589
+ (error) => {
8590
+ if (onError) {
8591
+ try {
8592
+ const p = onError(error, txHash);
8593
+ if (p && typeof p.catch === "function") {
8594
+ p.catch(() => {
8595
+ });
8596
+ }
8597
+ } catch {
8598
+ }
8599
+ }
8600
+ }
8601
+ );
8602
+ }
8603
+ async function runPreSendScreen(kontext, input) {
8604
+ const result = await kontext.verify({ ...input, txHash: "pre-screening" });
8605
+ if (!result.compliant) {
8606
+ throw new ViemComplianceError(
8607
+ `Transaction blocked: ${result.recommendations?.[0] ?? "compliance check failed"}`,
8608
+ result,
8609
+ { from: input.from, to: input.to, amount: input.amount }
8610
+ );
8611
+ }
8612
+ }
8613
+ function decodeTransferCalldata(data) {
8614
+ if (!data || data.length < 10) return null;
8615
+ const selector = data.slice(0, 10).toLowerCase();
8616
+ if (selector === TRANSFER_SELECTOR && data.length >= 138) {
8617
+ const to = "0x" + data.slice(34, 74);
8618
+ const amount = BigInt("0x" + data.slice(74, 138));
8619
+ return { to, amount };
8620
+ }
8621
+ if (selector === TRANSFER_FROM_SELECTOR && data.length >= 202) {
8622
+ const from = "0x" + data.slice(34, 74);
8623
+ const to = "0x" + data.slice(98, 138);
8624
+ const amount = BigInt("0x" + data.slice(138, 202));
8625
+ return { from, to, amount };
8626
+ }
8627
+ return null;
8628
+ }
8629
+ function decodeWriteContractArgs(functionName, args) {
8630
+ if (!args || !Array.isArray(args)) return null;
8631
+ if (functionName === "transfer" && args.length >= 2) {
8632
+ return { to: String(args[0]), amount: BigInt(args[1]) };
8633
+ }
8634
+ if (functionName === "transferFrom" && args.length >= 3) {
8635
+ return { from: String(args[0]), to: String(args[1]), amount: BigInt(args[2]) };
8636
+ }
8637
+ return null;
8638
+ }
8639
+ function formatTokenAmount2(amount, decimals) {
8640
+ const divisor = BigInt(10 ** decimals);
8641
+ const whole = amount / divisor;
8642
+ const fraction = amount % divisor;
8643
+ if (fraction === 0n) return whole.toString();
8644
+ const fractionStr = fraction.toString().padStart(decimals, "0").replace(/0+$/, "");
8645
+ return `${whole}.${fractionStr}`;
8646
+ }
8647
+
8648
+ export { AgentIdentityRegistry, AnomalyDetector, BehavioralFingerprinter, CHAIN_ID_MAP, CURRENCY_REQUIRED_LISTS, ChainalysisFreeAPIProvider, ChainalysisOracleProvider, CircleWalletManager, CoinbaseWalletManager, ConsoleExporter, CrossSessionLinker, DigestChain, FeatureFlagManager, FileStorage, JsonFileExporter, KONTEXT_BUILDER_CODE, KYAConfidenceScorer, Kontext, KontextError, KontextErrorCode, MemoryStorage, MetaMaskWalletManager, NoopExporter, OFACAddressProvider, OFACEntityProvider, OnChainExporter, OpenSanctionsLocalProvider, OpenSanctionsProvider, PLAN_LIMITS, PaymentCompliance, PlanManager, ProvenanceManager, STABLECOIN_CONTRACTS, ScreeningAggregator, TOKEN_REQUIRED_LISTS, TrustScorer, UKOFSIProvider, UsdcCompliance, ViemComplianceError, WalletClusterer, WalletMonitor, anchorDigest, encodeERC8021Suffix, exchangeAttestation, fetchAgentCard, fetchTransactionAttribution, getAnchor, getRequiredLists, isBlockchainAddress, isCryptoTransaction, isFeatureAvailable, loadConfigFile, parseERC8021Suffix, providerSupportsQuery, requirePlan, verifyAnchor, verifyExportedChain, withKontextCompliance };
7481
8649
  //# sourceMappingURL=index.mjs.map
7482
8650
  //# sourceMappingURL=index.mjs.map