@zyfai/sdk 0.2.37 → 0.2.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -47,6 +47,7 @@ var import_axios = __toESM(require("axios"));
47
47
  // src/config/endpoints.ts
48
48
  var API_ENDPOINT = "https://api.zyf.ai";
49
49
  var DATA_API_ENDPOINT = "https://defiapi.zyf.ai";
50
+ var WS_ENDPOINT = "wss://defiapi.zyf.ai/ws/events";
50
51
  var API_VERSION = "/api/v1";
51
52
  var DATA_API_VERSION = "/api/v2";
52
53
  var ENDPOINTS = {
@@ -80,6 +81,21 @@ var ENDPOINTS = {
80
81
  SDK_TVL: "/data/sdk-tvl",
81
82
  // Agent Identity Registry
82
83
  AGENT_TOKEN_URI: "/users/me/agent-token-uri",
84
+ // Simulation
85
+ SIMULATE_BEST_POSITIONS: (params) => {
86
+ const networks = Array.isArray(params.networks) ? params.networks.join(",") : params.networks;
87
+ const query = [
88
+ `amount=${params.amount}`,
89
+ `token=${params.token}`,
90
+ `networks=${networks}`,
91
+ `strategy=${params.strategy}`
92
+ ];
93
+ if (params.minSplit !== void 0) query.push(`minSplit=${params.minSplit}`);
94
+ if (params.protocols?.length) query.push(`protocols=${params.protocols.join(",")}`);
95
+ if (params.pools?.length) query.push(`pools=${params.pools.join(",")}`);
96
+ if (params.userPositions?.length) query.push(`userPositions=${encodeURIComponent(JSON.stringify(params.userPositions))}`);
97
+ return `/simulate/best-positions?${query.join("&")}`;
98
+ },
83
99
  // Customization
84
100
  CUSTOMIZE_BATCH: "/customization/customize-batch",
85
101
  CUSTOMIZATION_POOLS: (protocolId, strategy) => `/customization/pools?protocolId=${protocolId}${strategy ? `&strategy=${strategy}` : ""}`,
@@ -476,41 +492,34 @@ var VAULT_ABI = [
476
492
  ];
477
493
  var VAULT_ADDRESS = "0xD580071c47d4a667858B5FafAb85BC9C609beC5D";
478
494
 
495
+ // src/config/constants.ts
496
+ var MIN_PORTFOLIO_BALANCE = {
497
+ 1: {
498
+ USDC: 10000n * 10n ** 6n,
499
+ // 10,000 USDC (6 decimals)
500
+ WETH: 5n * 10n ** 18n
501
+ // 5 WETH (18 decimals)
502
+ }
503
+ };
504
+ var formatMinPortfolioLabel = (raw, decimals, symbol) => {
505
+ const whole = raw / 10n ** BigInt(decimals);
506
+ return `${whole.toString()} ${symbol}`;
507
+ };
508
+
479
509
  // src/core/ZyfaiSDK.ts
480
510
  var import_accounts2 = require("viem/accounts");
481
- var import_viem5 = require("viem");
511
+ var import_viem4 = require("viem");
482
512
 
483
513
  // src/config/chains.ts
484
514
  var import_viem2 = require("viem");
485
515
  var import_chains = require("viem/chains");
486
- var import_viem3 = require("viem");
487
- var plasma = (0, import_viem3.defineChain)({
488
- id: 9745,
489
- name: "Plasma",
490
- nativeCurrency: {
491
- decimals: 18,
492
- name: "Plasma",
493
- symbol: "PLSM"
494
- },
495
- rpcUrls: {
496
- default: {
497
- http: ["https://rpc.plasma.to"]
498
- }
499
- },
500
- blockExplorers: {
501
- default: {
502
- name: "Plasma Explorer",
503
- url: "https://explorer.plasma.io"
504
- }
505
- }
506
- });
507
516
  var DEFAULT_TOKEN_ADDRESSES = {
517
+ 1: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
518
+ // USDC on Ethereum Mainnet
508
519
  8453: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
509
520
  // USDC on Base
510
- 42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831",
521
+ 42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
511
522
  // USDC on Arbitrum
512
- 9745: "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb"
513
- // USDT on Plasma
514
523
  };
515
524
  var ASSET_CONFIGS = {
516
525
  USDC: {
@@ -521,25 +530,23 @@ var ASSET_CONFIGS = {
521
530
  decimals: 6,
522
531
  tokenSymbols: ["USDC", "USDC.e", "USDT", "USDT0"],
523
532
  tokenSymbolsByChainId: {
533
+ 1: "USDC",
524
534
  8453: "USDC",
525
535
  42161: "USDC",
526
- 9745: "USDT0",
527
536
  146: "USDC.e",
528
- 1: "USDC"
537
+ 59144: "USDC"
529
538
  },
530
539
  addresses: {
540
+ 1: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
541
+ // Ethereum
531
542
  8453: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
532
543
  // Base
533
544
  42161: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
534
545
  // Arbitrum
535
- 9745: "0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb",
536
- // Plasma
537
546
  146: "0x29219dd400f2bf60e5a23d13be72b486d4038894",
538
547
  // Sonic
539
- 59144: "0x176211869ca2b568f2a7d4ee941e073a821ee1ff",
548
+ 59144: "0x176211869ca2b568f2a7d4ee941e073a821ee1ff"
540
549
  // Linea
541
- 1: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
542
- // Ethereum
543
550
  },
544
551
  enabled: true
545
552
  },
@@ -551,26 +558,23 @@ var ASSET_CONFIGS = {
551
558
  decimals: 18,
552
559
  tokenSymbols: ["WETH", "ETH"],
553
560
  tokenSymbolsByChainId: {
561
+ 1: "WETH",
554
562
  8453: "WETH",
555
563
  42161: "WETH",
556
- 9745: "WETH",
557
564
  146: "WETH",
558
- 59144: "WETH",
559
- 1: "WETH"
565
+ 59144: "WETH"
560
566
  },
561
567
  addresses: {
568
+ 1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
569
+ // Ethereum
562
570
  8453: "0x4200000000000000000000000000000000000006",
563
571
  // Base
564
572
  42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
565
573
  // Arbitrum
566
- 9745: "0x4200000000000000000000000000000000000006",
567
- // Plasma
568
574
  146: "0x039e64f90d4199560e7533692f69448878db85c7",
569
575
  // Sonic
570
- 59144: "0xe5d7c2a44ffddf6b295a15c148167daaaf5cf34f",
576
+ 59144: "0xe5d7c2a44ffddf6b295a15c148167daaaf5cf34f"
571
577
  // Linea
572
- 1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
573
- // Ethereum
574
578
  },
575
579
  enabled: true
576
580
  }
@@ -585,14 +589,14 @@ var getDefaultTokenAddress = (chainId, asset) => {
585
589
  return address;
586
590
  };
587
591
  var DEFAULT_RPC_URLS = {
592
+ 1: "https://mainnet.infura.io/v3/8e6cdd06e30d40ac9990bf61bed3a3d0",
588
593
  8453: "https://mainnet.base.org",
589
- 42161: "https://arb1.arbitrum.io/rpc",
590
- 9745: "https://rpc.plasma.to"
594
+ 42161: "https://arb1.arbitrum.io/rpc"
591
595
  };
592
596
  var CHAINS = {
597
+ 1: import_chains.mainnet,
593
598
  8453: import_chains.base,
594
- 42161: import_chains.arbitrum,
595
- 9745: plasma
599
+ 42161: import_chains.arbitrum
596
600
  };
597
601
  var getChainConfig = (chainId, rpcUrls) => {
598
602
  const chain = CHAINS[chainId];
@@ -620,7 +624,7 @@ var getSupportedChainIds = () => {
620
624
  // src/utils/safe-account.ts
621
625
  var import_module_sdk = require("@rhinestone/module-sdk");
622
626
  var import_accounts = require("permissionless/accounts");
623
- var import_viem4 = require("viem");
627
+ var import_viem3 = require("viem");
624
628
  var import_account_abstraction = require("viem/account-abstraction");
625
629
  var SAFE_7579_ADDRESS = "0x7579EE8307284F293B1927136486880611F20002";
626
630
  var ERC7579_LAUNCHPAD_ADDRESS = "0x7579011aB74c46090561ea277Ba79D510c6C00ff";
@@ -630,12 +634,12 @@ var getSafeAccount = async (config) => {
630
634
  if (!safeOwnerAddress) {
631
635
  throw new Error("Safe owner address is required");
632
636
  }
633
- const formattedOwnerAddress = (0, import_viem4.getAddress)(safeOwnerAddress);
637
+ const formattedOwnerAddress = (0, import_viem3.getAddress)(safeOwnerAddress);
634
638
  const ownableValidator = (0, import_module_sdk.getOwnableValidator)({
635
639
  owners: [formattedOwnerAddress],
636
640
  threshold: 1
637
641
  });
638
- const saltHex = (0, import_viem4.fromHex)((0, import_viem4.toHex)(ACCOUNT_SALT), "bigint");
642
+ const saltHex = (0, import_viem3.fromHex)((0, import_viem3.toHex)(ACCOUNT_SALT), "bigint");
639
643
  const tempOwner = {
640
644
  address: formattedOwnerAddress,
641
645
  type: "json-rpc"
@@ -939,7 +943,7 @@ var _ZyfaiSDK = class _ZyfaiSDK {
939
943
  return;
940
944
  }
941
945
  const walletClient = this.getWalletClient();
942
- const userAddress = (0, import_viem5.getAddress)(walletClient.account.address);
946
+ const userAddress = (0, import_viem4.getAddress)(walletClient.account.address);
943
947
  const chainId = this.currentChainId || walletClient.chain?.id || 8453;
944
948
  const challengeResponse = await this.httpClient.post(ENDPOINTS.AUTH_CHALLENGE, {});
945
949
  let uri;
@@ -1293,10 +1297,10 @@ var _ZyfaiSDK = class _ZyfaiSDK {
1293
1297
  this.walletClient.chain?.id || 8453,
1294
1298
  this.rpcUrls
1295
1299
  );
1296
- this.walletClient = (0, import_viem5.createWalletClient)({
1300
+ this.walletClient = (0, import_viem4.createWalletClient)({
1297
1301
  account: newAddress,
1298
1302
  chain: chainConfig.chain,
1299
- transport: (0, import_viem5.custom)(this.currentProvider)
1303
+ transport: (0, import_viem4.custom)(this.currentProvider)
1300
1304
  });
1301
1305
  this.currentChainId = this.walletClient.chain?.id || null;
1302
1306
  try {
@@ -1348,10 +1352,10 @@ var _ZyfaiSDK = class _ZyfaiSDK {
1348
1352
  }
1349
1353
  this.signer = (0, import_accounts2.privateKeyToAccount)(privateKey);
1350
1354
  this.currentChainId = chainId;
1351
- this.walletClient = (0, import_viem5.createWalletClient)({
1355
+ this.walletClient = (0, import_viem4.createWalletClient)({
1352
1356
  account: this.signer,
1353
1357
  chain: chainConfig.chain,
1354
- transport: (0, import_viem5.http)(chainConfig.rpcUrl)
1358
+ transport: (0, import_viem4.http)(chainConfig.rpcUrl)
1355
1359
  });
1356
1360
  connectedAddress = this.signer.address;
1357
1361
  this.currentProvider = null;
@@ -1369,10 +1373,10 @@ var _ZyfaiSDK = class _ZyfaiSDK {
1369
1373
  if (!accounts || accounts.length === 0) {
1370
1374
  throw new Error("No accounts found in wallet provider");
1371
1375
  }
1372
- this.walletClient = (0, import_viem5.createWalletClient)({
1376
+ this.walletClient = (0, import_viem4.createWalletClient)({
1373
1377
  account: accounts[0],
1374
1378
  chain: chainConfig.chain,
1375
- transport: (0, import_viem5.custom)(provider)
1379
+ transport: (0, import_viem4.custom)(provider)
1376
1380
  });
1377
1381
  connectedAddress = accounts[0];
1378
1382
  this.currentProvider = provider;
@@ -1381,7 +1385,7 @@ var _ZyfaiSDK = class _ZyfaiSDK {
1381
1385
  provider.on("accountsChanged", this.handleAccountsChanged.bind(this));
1382
1386
  }
1383
1387
  } else if (provider.account && provider.transport) {
1384
- this.walletClient = (0, import_viem5.createWalletClient)({
1388
+ this.walletClient = (0, import_viem4.createWalletClient)({
1385
1389
  account: provider.account,
1386
1390
  chain: chainConfig.chain,
1387
1391
  transport: provider.transport
@@ -1434,10 +1438,10 @@ var _ZyfaiSDK = class _ZyfaiSDK {
1434
1438
  targetChainId,
1435
1439
  this.rpcUrls
1436
1440
  );
1437
- return (0, import_viem5.createWalletClient)({
1441
+ return (0, import_viem4.createWalletClient)({
1438
1442
  account: this.signer,
1439
1443
  chain: targetChainConfig.chain,
1440
- transport: (0, import_viem5.http)(targetChainConfig.rpcUrl)
1444
+ transport: (0, import_viem4.http)(targetChainConfig.rpcUrl)
1441
1445
  });
1442
1446
  } else {
1443
1447
  if (!this.walletClient) {
@@ -1800,22 +1804,30 @@ var _ZyfaiSDK = class _ZyfaiSDK {
1800
1804
  * Deposit funds from EOA to Safe smart wallet
1801
1805
  * Transfers tokens from the connected wallet to the user's Safe and logs the deposit
1802
1806
  *
1803
- * Token is automatically selected based on chain:
1804
- * - Base (8453) and Arbitrum (42161): USDC
1805
- * - Plasma (9745): USDT
1807
+ * Token is automatically selected based on chain (USDC by default):
1808
+ * - Ethereum Mainnet (1), Base (8453), Arbitrum (42161)
1809
+ *
1810
+ * Minimum portfolio balance enforced (Safe balance + deposit amount),
1811
+ * configured per-chain in `MIN_PORTFOLIO_BALANCE`:
1812
+ * - Ethereum Mainnet (1): 5 USDC (test threshold)
1813
+ * - Base (8453) & Arbitrum (42161): no minimum
1814
+ *
1815
+ * If no minimum is configured for a given (chainId, asset) pair,
1816
+ * the check is skipped.
1806
1817
  *
1807
1818
  * @param userAddress - User's address (owner of the Safe)
1808
1819
  * @param chainId - Target chain ID
1809
1820
  * @param amount - Amount in least decimal units (e.g., "100000000" for 100 USDC with 6 decimals)
1821
+ * @param asset - Optional asset symbol ("USDC" or "WETH"). Defaults to "USDC".
1810
1822
  * @returns Deposit response with transaction hash
1811
1823
  *
1812
1824
  * @example
1813
1825
  * ```typescript
1814
- * // Deposit 100 USDC (6 decimals) to Safe on Base
1826
+ * // Deposit 10,000 USDC (6 decimals) to Safe on Base
1815
1827
  * const result = await sdk.depositFunds(
1816
1828
  * "0xUser...",
1817
1829
  * 8453,
1818
- * "100000000" // 100 USDC = 100 * 10^6
1830
+ * "10000000000" // 10,000 USDC = 10_000 * 10^6
1819
1831
  * );
1820
1832
  * ```
1821
1833
  */
@@ -1830,7 +1842,15 @@ var _ZyfaiSDK = class _ZyfaiSDK {
1830
1842
  if (!amount || isNaN(Number(amount)) || Number(amount) <= 0) {
1831
1843
  throw new Error("Valid amount is required");
1832
1844
  }
1833
- const token = getDefaultTokenAddress(chainId, asset);
1845
+ const assetSymbol = (asset || "USDC").toUpperCase();
1846
+ const assetConfig = ASSET_CONFIGS[assetSymbol];
1847
+ if (!assetConfig) {
1848
+ throw new Error(
1849
+ `Unsupported asset: ${assetSymbol}. Supported: USDC, WETH.`
1850
+ );
1851
+ }
1852
+ const minRequired = MIN_PORTFOLIO_BALANCE[chainId]?.[assetSymbol];
1853
+ const token = getDefaultTokenAddress(chainId, assetSymbol);
1834
1854
  const walletClient = this.getWalletClient();
1835
1855
  const chainConfig = getChainConfig(chainId, this.rpcUrls);
1836
1856
  const safeAddress = await getDeterministicSafeAddress({
@@ -1848,6 +1868,25 @@ var _ZyfaiSDK = class _ZyfaiSDK {
1848
1868
  );
1849
1869
  }
1850
1870
  const amountBigInt = BigInt(amount);
1871
+ if (minRequired !== void 0) {
1872
+ const currentSafeBalance = await chainConfig.publicClient.readContract({
1873
+ address: token,
1874
+ abi: ERC20_ABI,
1875
+ functionName: "balanceOf",
1876
+ args: [safeAddress]
1877
+ });
1878
+ const totalAfterDeposit = currentSafeBalance + amountBigInt;
1879
+ if (totalAfterDeposit < minRequired) {
1880
+ const minLabel = formatMinPortfolioLabel(
1881
+ minRequired,
1882
+ assetConfig.decimals,
1883
+ assetSymbol
1884
+ );
1885
+ throw new Error(
1886
+ `Minimum portfolio balance not met on chain ${chainId}. Total portfolio must be at least ${minLabel}. Current Safe balance: ${currentSafeBalance.toString()}, deposit amount: ${amountBigInt.toString()}, total after deposit: ${totalAfterDeposit.toString()} (raw units, ${assetConfig.decimals} decimals).`
1887
+ );
1888
+ }
1889
+ }
1851
1890
  const txHash = await walletClient.writeContract({
1852
1891
  address: token,
1853
1892
  abi: ERC20_ABI,
@@ -1895,8 +1934,7 @@ var _ZyfaiSDK = class _ZyfaiSDK {
1895
1934
  * - Need more control over transaction execution
1896
1935
  *
1897
1936
  * Token is automatically selected based on chain if not provided:
1898
- * - Base (8453) and Arbitrum (42161): USDC
1899
- * - Plasma (9745): USDT
1937
+ * - Ethereum Mainnet (1), Base (8453), Arbitrum (42161): USDC by default
1900
1938
  *
1901
1939
  * @param chainId - Chain ID where the deposit was made
1902
1940
  * @param txHash - Transaction hash of the deposit
@@ -3073,6 +3111,54 @@ var _ZyfaiSDK = class _ZyfaiSDK {
3073
3111
  );
3074
3112
  }
3075
3113
  }
3114
+ // ============================================================================
3115
+ // Simulation
3116
+ // ============================================================================
3117
+ /**
3118
+ * Simulate the best yield positions for a given amount, splitting it across
3119
+ * the top-ranked pools (up to minSplit positions) for the requested chains/strategy.
3120
+ *
3121
+ * Returns ready-to-execute calldata (approve + deposit) per position. The deposit
3122
+ * calldata contains a "<RECEIVER>" placeholder that must be replaced with the
3123
+ * actual receiving address (e.g. the user's Safe) before sending the transaction.
3124
+ *
3125
+ * @param params - Simulation parameters
3126
+ * @returns Chain-keyed map of simulated positions with calldata
3127
+ *
3128
+ * @example
3129
+ * ```typescript
3130
+ * const result = await sdk.simulateBestPositions({
3131
+ * amount: 3000,
3132
+ * token: "USDC",
3133
+ * networks: 8453,
3134
+ * strategy: "conservative",
3135
+ * minSplit: 3,
3136
+ * protocols: ["aave", "compound", "morpho"],
3137
+ * });
3138
+ * console.log(result.data["8453"]);
3139
+ * ```
3140
+ */
3141
+ async simulateBestPositions(params) {
3142
+ try {
3143
+ if (!isValidPublicStrategy(params.strategy)) {
3144
+ throw new Error(
3145
+ `Invalid strategy: ${params.strategy}. Must be "conservative" or "aggressive".`
3146
+ );
3147
+ }
3148
+ const internalStrategy = toInternalStrategy(params.strategy);
3149
+ const response = await this.httpClient.get(
3150
+ ENDPOINTS.SIMULATE_BEST_POSITIONS({
3151
+ ...params,
3152
+ strategy: internalStrategy
3153
+ })
3154
+ );
3155
+ return response;
3156
+ } catch (error) {
3157
+ throw new Error(
3158
+ `Failed to simulate best positions: ${error.message}`
3159
+ );
3160
+ }
3161
+ }
3076
3162
  /**
3077
3163
  * Get currently selected pools for a protocol on a specific chain.
3078
3164
  *
@@ -3149,7 +3235,7 @@ var _ZyfaiSDK = class _ZyfaiSDK {
3149
3235
  if (!tokenUriResponse.tokenUri) {
3150
3236
  throw new Error("API did not return a tokenUri");
3151
3237
  }
3152
- const callData = (0, import_viem5.encodeFunctionData)({
3238
+ const callData = (0, import_viem4.encodeFunctionData)({
3153
3239
  abi: IDENTITY_REGISTRY_ABI,
3154
3240
  functionName: "register",
3155
3241
  args: [tokenUriResponse.tokenUri]
@@ -3515,6 +3601,78 @@ var _ZyfaiSDK = class _ZyfaiSDK {
3515
3601
  symbol: tokenSymbol
3516
3602
  };
3517
3603
  }
3604
+ // ============================================================================
3605
+ // WebSocket Event Stream
3606
+ // ============================================================================
3607
+ /**
3608
+ * Subscribe to real-time Zyfai risk and liquidity events via WebSocket.
3609
+ *
3610
+ * Events: depeg, liquidity_trap, liquidity_restored, pool_status_change,
3611
+ * new_collateral_detected, liquidity_drop.
3612
+ *
3613
+ * @param handlers - Callback per event type, plus onError
3614
+ * @param filters - Optional protocol/pool filter sent to the server on subscribe
3615
+ * @returns Cleanup function — call it to close the connection
3616
+ *
3617
+ * @example
3618
+ * ```typescript
3619
+ * const unsubscribe = sdk.subscribeToEvents(
3620
+ * {
3621
+ * onDepeg: (data) => console.log("Depeg detected:", data.token, data.severity),
3622
+ * onLiquidityDrop: (data) => console.log("Liquidity drop:", data.pool, data.dropPercent),
3623
+ * },
3624
+ * { protocols: ["morpho"], pools: ["gauntlet usdc core"] }
3625
+ * );
3626
+ *
3627
+ * // Later, close the connection:
3628
+ * unsubscribe();
3629
+ * ```
3630
+ */
3631
+ subscribeToEvents(handlers, filters) {
3632
+ let ws = null;
3633
+ let closed = false;
3634
+ const connect = () => {
3635
+ ws = new globalThis.WebSocket(WS_ENDPOINT);
3636
+ ws.onopen = () => {
3637
+ const msg = { type: "subscribe" };
3638
+ if (filters && (filters.chains?.length || filters.protocols?.length || filters.pools?.length)) {
3639
+ msg.filters = filters;
3640
+ }
3641
+ ws.send(JSON.stringify(msg));
3642
+ };
3643
+ ws.onmessage = (event) => {
3644
+ try {
3645
+ const msg = JSON.parse(event.data);
3646
+ if (msg.type !== "event") return;
3647
+ switch (msg.eventType) {
3648
+ case "depeg":
3649
+ handlers.onDepeg?.(msg.data);
3650
+ break;
3651
+ case "new_collateral_detected":
3652
+ handlers.onNewCollateralDetected?.(msg.data);
3653
+ break;
3654
+ case "liquidity_drop":
3655
+ handlers.onLiquidityDrop?.(msg.data);
3656
+ break;
3657
+ }
3658
+ } catch {
3659
+ }
3660
+ };
3661
+ ws.onerror = (error) => {
3662
+ handlers.onError?.(error);
3663
+ };
3664
+ ws.onclose = () => {
3665
+ if (!closed) {
3666
+ setTimeout(connect, 3e3);
3667
+ }
3668
+ };
3669
+ };
3670
+ connect();
3671
+ return () => {
3672
+ closed = true;
3673
+ ws?.close();
3674
+ };
3675
+ }
3518
3676
  };
3519
3677
  // ============================================================================
3520
3678
  // Agent Identity Registry