import { encryptAndStore, generateWallet, getConfirmationWords, getStorageInfo, validateConfirmation } from "./keychain-wallet.mjs";
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
//#region extensions/crypto/src/services/onboarding-flow.ts
/**
* OpenClawnch Onboarding Flow — first-run tutorial state machine.
*
* Detects new users and walks them through:
* 1. Welcome — professional greeting with capabilities overview
* 2. Persona selection — choose communication style (professional, degen, chill, etc.)
* 3. Capabilities overview — show what's available, note what needs deploy-time config
* 4. Wallet connect — pair a mobile wallet via deep link
* 5. First read action — try a read-only query
* 6. First write action — try a transaction (user approves on phone)
* 7. Complete — command reference card
*
* All infrastructure config (LLM keys, WalletConnect project ID, etc.) is handled
* at deploy time via `openclawnch deploy`. This flow only handles user preferences
* and wallet pairing.
*
* State persists on volume so interrupted tutorials resume.
*/
const PERSONAS = [
	{
		id: "professional",
		label: "1. Professional",
		description: "Clear, concise, business-like. Sticks to facts and figures.",
		example: "\"Your portfolio is up 3.2% today. ETH is at $3,847. Shall I proceed with the swap?\""
	},
	{
		id: "degen",
		label: "2. Degen",
		description: "CT native. Speaks the language of crypto twitter.",
		example: "\"ser that token is absolutely ripping rn 🚀 ape in or stay poor, your call anon\""
	},
	{
		id: "chill",
		label: "3. Chill",
		description: "Relaxed, friendly, no pressure. Like texting a knowledgeable friend.",
		example: "\"hey so ETH is looking pretty good today, up about 3%. want me to grab some?\""
	},
	{
		id: "technical",
		label: "4. Technical",
		description: "Detailed, data-heavy. Includes on-chain metrics and technical analysis.",
		example: "\"ETH/USD at $3,847.32, 24h vol $18.2B, RSI 62.4. Gas at 12 gwei. The 0.3% Uniswap V3 pool has $142M TVL with concentrated liquidity at 3800-3900.\""
	},
	{
		id: "mentor",
		label: "5. Mentor",
		description: "Educational. Explains concepts as it goes, good for DeFi newcomers.",
		example: "\"I'll swap your ETH for USDC. Quick explainer: this goes through a DEX (decentralized exchange), which means no middleman — just a smart contract matching your trade. You'll approve it on your phone.\""
	}
];
const CAPABILITIES = [
	{
		id: "wallet",
		name: "Wallet & Transactions",
		description: "Connect your wallet, send tokens, approve transactions from your phone.",
		tools: [
			"clawnchconnect",
			"transfer",
			"permit2"
		],
		deployRequirement: "WALLETCONNECT_PROJECT_ID",
		needsWallet: true
	},
	{
		id: "prices",
		name: "Prices & Market Data",
		description: "Real-time token prices, trending coins, market intelligence.",
		tools: [
			"defi_price",
			"market_intel",
			"herd_intelligence",
			"analytics"
		],
		needsWallet: false
	},
	{
		id: "portfolio",
		name: "Portfolio & Balance Tracking",
		description: "View balances, track cost basis, and monitor your positions.",
		tools: [
			"defi_balance",
			"cost_basis",
			"watch_activity",
			"block_explorer"
		],
		needsWallet: false
	},
	{
		id: "trading",
		name: "DEX Trading & Swaps",
		description: "Execute token swaps via DEX aggregators with best-price routing.",
		tools: [
			"defi_swap",
			"manage_orders",
			"crypto_workflow"
		],
		needsWallet: true
	},
	{
		id: "liquidity",
		name: "Liquidity Provision",
		description: "Manage Uniswap V3/V4 liquidity positions, add/remove liquidity.",
		tools: ["liquidity"],
		needsWallet: true
	},
	{
		id: "launchpad",
		name: "Token Launchpad (Clawnch)",
		description: "Launch new tokens on Base with Uniswap V4 pools and manage fee revenue.",
		tools: [
			"clawnch_launch",
			"clawnch_fees",
			"clawnch_info"
		],
		needsWallet: true
	},
	{
		id: "bridge",
		name: "Cross-Chain Bridge",
		description: "Bridge tokens across Ethereum, Base, Arbitrum, Optimism, and other chains.",
		tools: ["bridge"],
		needsWallet: true
	},
	{
		id: "routing",
		name: "Smart Routing (Wayfinder)",
		description: "AI-powered route optimization across chains and protocols.",
		tools: ["wayfinder"],
		needsWallet: false
	},
	{
		id: "clawnx",
		name: "ClawnX Protocol",
		description: "Interact with the ClawnX decentralized exchange protocol.",
		tools: ["clawnx"],
		needsWallet: true
	},
	{
		id: "hummingbot",
		name: "Market Making (Hummingbot)",
		description: "Automated market making and trading bot management.",
		tools: ["hummingbot"],
		deployRequirement: "HUMMINGBOT_API_URL",
		needsWallet: false
	}
];
/** Resolve the state dir at call time so env changes (e.g. in tests) are respected. */
function getStateDir() {
	return process.env.OPENCLAWNCH_TX_DIR ? join(process.env.OPENCLAWNCH_TX_DIR, "..", "onboarding") : join(process.env.HOME ?? "/tmp", ".openclawnch", "onboarding");
}
function ensureStateDir() {
	const dir = getStateDir();
	if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
}
function sanitizeUserId(userId) {
	const safe = userId.replace(/[^a-zA-Z0-9_\-\.]/g, "_");
	if (safe.includes("..") || safe.includes("/") || safe.includes("\\")) return "invalid_user";
	return safe.slice(0, 64);
}
function statePath(userId) {
	return join(getStateDir(), `${sanitizeUserId(userId)}.json`);
}
function loadState(userId) {
	try {
		const path = statePath(userId);
		if (!existsSync(path)) return null;
		return JSON.parse(readFileSync(path, "utf8"));
	} catch {
		return null;
	}
}
function saveState(state) {
	ensureStateDir();
	const { _pendingMnemonic, _pendingConfirmation, _pendingImportMnemonic, ...persistable } = state;
	writeFileSync(statePath(state.userId), JSON.stringify(persistable, null, 2), "utf8");
}
const WELCOME_MESSAGE = `OpenClawnch — personal DeFi agent. I handle real money on-chain, connected to your mobile wallet for approval on every transaction.

Pick a communication style to get started:

  /professional — Clear and business-like
  /degen — CT native energy
  /chill — Like texting a friend
  /technical — Data-heavy, on-chain metrics
  /mentor — Educational, explains as it goes

Or type your own preferred tone (10+ characters).

Explore what I can do:
  /help trading — Swaps, limit orders, DCA, leverage
  /help defi — Lending, staking, yield, liquidity
  /help portfolio — Balances, cost basis, tracking
  /help tools — User-defined tools, API connectors
  /help agents — Multi-agent delegation
  /help — Full command list

/skip — Jump straight in`;
function buildPersonaConfirmation(persona, customText) {
	return `${persona === "custom" ? `Got it. I'll communicate in your preferred style: "${customText}"` : `${PERSONAS.find((p) => p.id === persona)?.label.replace(/^\d+\.\s*/, "") ?? persona} mode selected.`}

Now pick your capabilities:

${buildCapabilitiesList()}

/all — Enable everything

Or type numbers (e.g. "1, 2, 3, 5")

/skip — Skip and use defaults`;
}
function buildCapabilitiesList() {
	return CAPABILITIES.map((c, i) => {
		const status = getCapabilityStatus(c);
		return `  ${i + 1}. /cap_${c.id} — ${c.name} ${status}\n     ${c.description}`;
	}).join("\n\n");
}
/** Check if a capability's deploy-time requirement is satisfied. */
function getCapabilityStatus(c) {
	if (!c.deployRequirement) return "";
	const envVal = process.env[c.deployRequirement];
	if (envVal && envVal.length > 0) return "";
	return `(needs ${c.deployRequirement} — see /setup)`;
}
function buildCapabilitiesConfirmation(selectedIds) {
	const selected = selectedIds.map((id) => CAPABILITIES.find((c) => c.id === id)).filter((c) => c != null);
	const ready = selected.filter((c) => !c.deployRequirement || (process.env[c.deployRequirement] ?? "").length > 0);
	const notConfigured = selected.filter((c) => c.deployRequirement && !(process.env[c.deployRequirement] ?? "").length);
	const walletNeeded = selected.some((c) => c.needsWallet);
	let msg = `Selected ${selected.length} capabilities.`;
	if (ready.length > 0) msg += `\n\nReady to use now:\n${ready.map((c) => `  - ${c.name}`).join("\n")}`;
	if (notConfigured.length > 0) msg += `\n\nNeeds deploy-time configuration:\n${notConfigured.map((c) => `  - ${c.name} — requires ${c.deployRequirement}\n    Set this environment variable in your deploy config.`).join("\n")}`;
	if (walletNeeded) msg += `\n\nNext step: connect your wallet. This lets me see your balances and propose transactions for you to approve on your phone.\n\nTap the link below to connect your wallet app (MetaMask, Rainbow, Coinbase Wallet, etc.), or type /connect.`;
	else msg += `\n\nYou can connect a wallet later with /connect if you want transaction capabilities.\n\nTry asking me something — for example: "What's the price of ETH?" or "What's trending on Base?"`;
	return msg;
}
const WALLET_CONNECTED_MESSAGE = (address, balance) => `Wallet connected: ${address.slice(0, 6)}...${address.slice(-4)}
Balance: ${balance}

Let's verify everything works. Try a read operation — ask me:
"What's trending on Base?"`;
const FIRST_READ_DONE_MESSAGE = `That's a read operation — instant, no wallet approval needed.

Now let's try a write operation. Say:
"Swap 0.001 ETH for USDC"

I'll prepare the transaction and send it to your phone wallet for approval. You always have final say.`;
const FIRST_WRITE_DONE_MESSAGE = `Transaction confirmed. Every write operation goes to your wallet for approval.

Setup complete. Explore what I can do:
  /help trading — Swaps, orders, DCA, leverage
  /help defi — Lending, staking, yield, bridging
  /help portfolio — Balances, prices, tracking
  /help — Full command list

Or just talk to me — "What's the price of ETH?", "Show my portfolio", "Send 10 USDC to 0x..."`;
const SKIP_MESSAGE = `Onboarding skipped. Use /help to explore commands, or /help <category> for specifics:
  trading, defi, portfolio, automation, wallet, fiat, tools, agents

  /connect — Pair wallet  |  /help — Full command list`;
function parsePersonaChoice(message) {
	const lower = message.toLowerCase().trim();
	if (/^1\b/.test(lower) || lower === "professional") return { persona: "professional" };
	if (/^2\b/.test(lower) || lower === "degen") return { persona: "degen" };
	if (/^3\b/.test(lower) || lower === "chill") return { persona: "chill" };
	if (/^4\b/.test(lower) || lower === "technical") return { persona: "technical" };
	if (/^5\b/.test(lower) || lower === "mentor") return { persona: "mentor" };
	if (message.trim().length >= 10) return {
		persona: "custom",
		customText: message.trim()
	};
	return null;
}
function parseCapabilityChoice(message) {
	const lower = message.toLowerCase().trim();
	if (lower === "all" || lower === "everything") return CAPABILITIES.map((c) => c.id);
	const numbers = message.match(/\d+/g);
	if (numbers && numbers.length > 0) {
		const ids = [];
		for (const n of numbers) {
			const idx = parseInt(n, 10) - 1;
			const cap = CAPABILITIES[idx];
			if (idx >= 0 && idx < CAPABILITIES.length && cap) ids.push(cap.id);
		}
		if (ids.length > 0) return [...new Set(ids)];
	}
	const ids = [];
	for (const cap of CAPABILITIES) if (lower.includes(cap.id) || lower.includes(cap.name.toLowerCase())) ids.push(cap.id);
	if (ids.length > 0) return [...new Set(ids)];
	return null;
}
var OnboardingFlow = class {
	state;
	constructor(userId) {
		const existing = loadState(userId);
		if (existing) this.state = existing;
		else {
			this.state = {
				userId,
				step: "welcome",
				walletConnected: false,
				firstReadDone: false,
				firstWriteDone: false,
				startedAt: Date.now(),
				lastInteraction: Date.now()
			};
			saveState(this.state);
		}
	}
	/** Is this user still in the onboarding flow? */
	get isActive() {
		if (this.state.step === "complete" || this.state.step === "skipped") return false;
		if (Date.now() - this.state.startedAt > 10080 * 60 * 1e3) {
			this.state.step = "skipped";
			this.state.lastInteraction = Date.now();
			saveState(this.state);
			return false;
		}
		return true;
	}
	/** Current step name. */
	get currentStep() {
		return this.state.step;
	}
	/** Get the current state (read-only). */
	getState() {
		return { ...this.state };
	}
	/**
	* Check if an incoming message is the user's first-ever message.
	* If so, return the welcome message. Otherwise return null.
	*/
	getWelcomeMessage() {
		if (this.state.step !== "welcome") return null;
		this.state.step = "choose_persona";
		this.state.lastInteraction = Date.now();
		saveState(this.state);
		return {
			text: WELCOME_MESSAGE,
			suggestion: "Reply with a number (1-5) or describe your preferred tone"
		};
	}
	/**
	* Navigate back one step during onboarding.
	* choose_capabilities → choose_persona
	* connect_wallet → choose_capabilities
	* first_read → connect_wallet
	* Returns null if /back isn't applicable at the current step.
	*/
	back() {
		const step = this.state.step;
		if (step === "choose_capabilities") {
			this.state.step = "choose_persona";
			this.state.lastInteraction = Date.now();
			saveState(this.state);
			return {
				text: "Pick a communication style:\n\n  /professional — Clear and business-like\n  /degen — CT native energy\n  /chill — Like texting a friend\n  /technical — Data-heavy, on-chain metrics\n  /mentor — Educational, explains as it goes\n\nOr type your own preferred tone (10+ characters).",
				suggestion: "Tap a style or describe your own"
			};
		}
		if (step === "connect_wallet") {
			this.state.step = "choose_capabilities";
			this.state.selectedCapabilities = void 0;
			this.state.lastInteraction = Date.now();
			saveState(this.state);
			return {
				text: buildPersonaConfirmation(this.state.persona ?? "professional", this.state.customPersona),
				suggestion: "Reply with numbers (e.g. \"1, 2, 3\") or \"all\""
			};
		}
		if (step === "first_read") {
			this.state.step = "connect_wallet";
			this.state.lastInteraction = Date.now();
			saveState(this.state);
			return {
				text: "Choose how to connect a wallet:\n\n  /create_wallet — Generate a new wallet (stored locally, encrypted)\n  /import_wallet — Import from a 12/24-word seed phrase\n  /connect — Connect MetaMask, Rainbow, Coinbase Wallet, etc. via WalletConnect\n  /connect_bankr — Use Bankr custodial wallet (zero setup)",
				suggestion: "Tap /create_wallet to get started or /connect for an existing wallet"
			};
		}
		return null;
	}
	/** Call when the user sends /skip-tutorial or /skip. */
	skip() {
		this.state.step = "skipped";
		this.state.completedAt = Date.now();
		this.state.lastInteraction = Date.now();
		saveState(this.state);
		return {
			text: SKIP_MESSAGE,
			final: true
		};
	}
	/** Reset onboarding to the beginning — shows the welcome message again. */
	restart() {
		this.state.step = "choose_persona";
		this.state.persona = void 0;
		this.state.completedAt = void 0;
		this.state.startedAt = Date.now();
		this.state.lastInteraction = Date.now();
		saveState(this.state);
		return { text: WELCOME_MESSAGE };
	}
	/**
	* Process a persona selection from the user.
	*/
	onPersonaSelected(message) {
		if (this.state.step !== "choose_persona") return null;
		const choice = parsePersonaChoice(message);
		if (!choice) return {
			text: "Pick a communication style:\n\n  /professional  /degen  /chill  /technical  /mentor\n\nOr type your own preferred tone.",
			suggestion: "Tap a style or describe your own"
		};
		this.state.persona = choice.persona;
		if (choice.customText) this.state.customPersona = choice.customText;
		this.state.step = "choose_capabilities";
		this.state.lastInteraction = Date.now();
		saveState(this.state);
		return {
			text: buildPersonaConfirmation(choice.persona, choice.customText),
			suggestion: "Reply with numbers (e.g. \"1, 2, 3\") or \"all\""
		};
	}
	/**
	* Process a capability selection from the user.
	* After selection, goes directly to wallet connect (if wallet capabilities selected)
	* or first_read (if no wallet needed). No guided setup step — all infrastructure
	* config is handled at deploy time.
	*/
	onCapabilitiesSelected(message) {
		if (this.state.step !== "choose_capabilities") return null;
		const ids = parseCapabilityChoice(message);
		if (!ids) return {
			text: `Tap a capability to select it, or type numbers (e.g. "1, 2, 3, 5"):\n\n${buildCapabilitiesList()}\n\n/all — Enable everything`,
			suggestion: "Tap a capability or type numbers"
		};
		this.state.selectedCapabilities = ids;
		this.state.lastInteraction = Date.now();
		if (ids.some((id) => {
			return CAPABILITIES.find((c) => c.id === id)?.needsWallet ?? false;
		})) {
			this.state.step = "connect_wallet";
			saveState(this.state);
			return {
				text: buildCapabilitiesConfirmation(ids) + `\n\nChoose how to connect a wallet:\n\n  /create_wallet — Generate a new wallet (stored locally, encrypted)\n  /import_wallet — Import from a 12/24-word seed phrase\n  /connect — Connect MetaMask, Rainbow, Coinbase Wallet, etc. via WalletConnect\n  /connect_bankr — Use Bankr custodial wallet (zero setup)`,
				showConnectLink: true,
				suggestion: "Tap /create_wallet to get started or /connect for an existing wallet"
			};
		}
		this.state.step = "first_read";
		saveState(this.state);
		return {
			text: buildCapabilitiesConfirmation(ids),
			suggestion: "What's the price of ETH?"
		};
	}
	/**
	* Start the "Create new wallet" flow. Generates a mnemonic and shows it.
	*/
	async onCreateWallet() {
		if (this.state.step !== "connect_wallet") return null;
		const wallet = await generateWallet();
		const confirmWords = getConfirmationWords(wallet.mnemonic);
		this.state._pendingMnemonic = wallet.mnemonic;
		this.state._pendingConfirmation = confirmWords;
		this.state.step = "create_wallet_confirm";
		this.state.lastInteraction = Date.now();
		saveState(this.state);
		const wordGrid = wallet.mnemonic.split(" ").map((w, i) => `  ${String(i + 1).padStart(2, " ")}. ${w}`).join("\n");
		const confirmPrompt = confirmWords.map((c) => `Word #${c.index}`).join(", ");
		return { text: `New wallet generated.\n\nAddress: \`${wallet.address}\`\n\nWrite down these 12 words — this is your only chance:\n\n${wordGrid}\n\nTo confirm you've saved them, type the following words separated by spaces:\n${confirmPrompt}` };
	}
	/**
	* Process mnemonic confirmation words during wallet creation.
	*/
	async onConfirmMnemonic(message) {
		if (this.state.step !== "create_wallet_confirm") return null;
		if (!this.state._pendingMnemonic || !this.state._pendingConfirmation) {
			this.state.step = "connect_wallet";
			saveState(this.state);
			return { text: "Wallet creation was interrupted (the previous mnemonic is no longer available). Run /create_wallet to generate a new wallet." };
		}
		const userWords = message.trim().split(/\s+/);
		const expected = this.state._pendingConfirmation;
		const confirmations = expected.map((exp, i) => ({
			index: exp.index,
			word: userWords[i] ?? ""
		}));
		if (!validateConfirmation(this.state._pendingMnemonic, confirmations)) return { text: `Incorrect. Please type the correct words for: ${expected.map((c) => `Word #${c.index}`).join(", ")}` };
		this.state.step = "create_wallet_password";
		this.state.lastInteraction = Date.now();
		saveState(this.state);
		return { text: "Mnemonic confirmed. Now set a password to encrypt your wallet (minimum 8 characters).\n\nThis password will be required to unlock your wallet each session.\n\n⚠️ Your password will be visible in chat history. Delete the message after sending." };
	}
	/**
	* Process password during wallet creation — encrypt and store.
	*/
	async onSetWalletPassword(password) {
		if (this.state.step !== "create_wallet_password") return null;
		if (!this.state._pendingMnemonic) {
			this.state.step = "connect_wallet";
			saveState(this.state);
			return { text: "Wallet creation was interrupted (the previous mnemonic is no longer available). Run /create_wallet to generate a new wallet." };
		}
		if (password.length < 8) return { text: "Password must be at least 8 characters. Try again." };
		try {
			await encryptAndStore(this.state._pendingMnemonic, password);
			const { mnemonicToAccount } = await import("../../accounts-CgUUF-_i.mjs");
			const address = mnemonicToAccount(this.state._pendingMnemonic).address;
			const storage = getStorageInfo();
			this.state._pendingMnemonic = void 0;
			this.state._pendingConfirmation = void 0;
			this.state.step = "first_read";
			this.state.walletConnected = true;
			this.state.walletAddress = address;
			this.state.lastInteraction = Date.now();
			saveState(this.state);
			return {
				text: `Wallet created and encrypted.\n\nAddress: \`${address}\`\nStorage: ${storage.backend === "keychain" ? "macOS Keychain" : `encrypted file (${storage.path})`}\n\nSend ETH or USDC to this address to get started.\n\nTry a read operation: "What's the price of ETH?"`,
				suggestion: "What's the price of ETH?"
			};
		} catch (err) {
			return { text: `Failed to encrypt wallet: ${err instanceof Error ? err.message : String(err)}. Try again.` };
		}
	}
	/**
	* Start the "Import existing wallet" flow — prompt for mnemonic.
	*/
	onImportWallet() {
		if (this.state.step !== "connect_wallet") return null;
		this.state.step = "import_wallet_mnemonic";
		this.state.lastInteraction = Date.now();
		saveState(this.state);
		return { text: "Paste your 12 or 24-word seed phrase (BIP-39 mnemonic).\n\nThe phrase is processed locally and never stored in plaintext.\n\n⚠️ Your seed phrase will be visible in chat history. Delete the message immediately after sending." };
	}
	/**
	* Process imported mnemonic — validate and ask for password.
	*/
	async onImportMnemonic(message) {
		if (this.state.step !== "import_wallet_mnemonic") return null;
		const words = message.trim().split(/\s+/);
		if (words.length !== 12 && words.length !== 24) return { text: `Expected 12 or 24 words, got ${words.length}. Please paste a valid BIP-39 mnemonic.` };
		try {
			const { mnemonicToAccount } = await import("../../accounts-CgUUF-_i.mjs");
			const account = mnemonicToAccount(words.join(" "));
			this.state._pendingImportMnemonic = words.join(" ");
			this.state.step = "import_wallet_password";
			this.state.lastInteraction = Date.now();
			saveState(this.state);
			return {
				text: `Valid mnemonic. Derived address: \`${account.address}\`\n\nSet a password to encrypt this wallet (minimum 8 characters).`,
				suggestion: "Type a strong password"
			};
		} catch (err) {
			return { text: `Invalid mnemonic: ${err instanceof Error ? err.message : String(err)}. Please try again.` };
		}
	}
	/**
	* Process password during import — encrypt and store.
	*/
	async onImportPassword(password) {
		if (this.state.step !== "import_wallet_password") return null;
		if (!this.state._pendingImportMnemonic) {
			this.state.step = "connect_wallet";
			saveState(this.state);
			return { text: "Import was interrupted. Please try /import_wallet again." };
		}
		if (password.length < 8) return { text: "Password must be at least 8 characters. Try again." };
		try {
			await encryptAndStore(this.state._pendingImportMnemonic, password);
			const { mnemonicToAccount } = await import("../../accounts-CgUUF-_i.mjs");
			const address = mnemonicToAccount(this.state._pendingImportMnemonic).address;
			const storage = getStorageInfo();
			this.state._pendingImportMnemonic = void 0;
			this.state.step = "first_read";
			this.state.walletConnected = true;
			this.state.walletAddress = address;
			this.state.lastInteraction = Date.now();
			saveState(this.state);
			return {
				text: `Wallet imported and encrypted.\n\nAddress: \`${address}\`\nStorage: ${storage.backend === "keychain" ? "macOS Keychain" : `encrypted file (${storage.path})`}\n\nTry a read operation: "What's the price of ETH?"`,
				suggestion: "What's the price of ETH?"
			};
		} catch (err) {
			return { text: `Failed to encrypt wallet: ${err instanceof Error ? err.message : String(err)}. Try again.` };
		}
	}
	/**
	* Call when wallet connection succeeds.
	* Returns the "wallet connected" message with balance info.
	*/
	onWalletConnected(address, balance) {
		if (this.state.step !== "connect_wallet") return null;
		this.state.step = "first_read";
		this.state.walletConnected = true;
		this.state.walletAddress = address;
		this.state.lastInteraction = Date.now();
		saveState(this.state);
		return {
			text: WALLET_CONNECTED_MESSAGE(address, balance),
			suggestion: "What's trending on Base?"
		};
	}
	/**
	* Call after the agent successfully completes a read operation.
	* Returns the "try a write" message if we're on that step.
	*/
	onReadComplete() {
		if (this.state.step !== "first_read") return null;
		this.state.step = "first_write";
		this.state.firstReadDone = true;
		this.state.lastInteraction = Date.now();
		saveState(this.state);
		return {
			text: FIRST_READ_DONE_MESSAGE,
			suggestion: "Swap 0.001 ETH for USDC"
		};
	}
	/**
	* Call after the agent successfully completes a write operation
	* (user approved the transaction).
	*/
	onWriteComplete() {
		if (this.state.step !== "first_write") return null;
		this.state.step = "complete";
		this.state.firstWriteDone = true;
		this.state.completedAt = Date.now();
		this.state.lastInteraction = Date.now();
		saveState(this.state);
		return {
			text: FIRST_WRITE_DONE_MESSAGE,
			final: true
		};
	}
	/**
	* Process an incoming message and return any onboarding-specific
	* response. Returns null if no onboarding action is needed.
	*/
	processMessage(message) {
		if (!this.isActive) return null;
		if (this.state.step === "welcome") return this.getWelcomeMessage();
		if (this.state.step === "create_wallet_confirm") return this.onConfirmMnemonic(message);
		if (this.state.step === "create_wallet_password") return this.onSetWalletPassword(message);
		if (this.state.step === "import_wallet_mnemonic") return this.onImportMnemonic(message);
		if (this.state.step === "import_wallet_password") return this.onImportPassword(message);
		return null;
	}
	/**
	* Determine if the agent should trigger an onboarding response
	* after a tool call completes. Hook into after_tool_call.
	*/
	processToolResult(toolName, success) {
		if (!this.isActive || !success) return null;
		if (this.state.step === "first_read" && [
			"defi_price",
			"market_intel",
			"defi_balance",
			"clawnch_info",
			"analytics",
			"herd_intelligence",
			"block_explorer",
			"watch_activity",
			"cost_basis",
			"clawnch_fees"
		].includes(toolName)) return this.onReadComplete();
		if (this.state.step === "first_write" && [
			"defi_swap",
			"transfer",
			"liquidity",
			"clawnch_launch",
			"bridge",
			"permit2",
			"defi_lend",
			"defi_stake",
			"compound_action",
			"manage_orders"
		].includes(toolName)) return this.onWriteComplete();
		return null;
	}
};
const flows = /* @__PURE__ */ new Map();
const MAX_FLOWS = 500;
/**
* Get (or create) the onboarding flow for a user.
* The userId should be the sender ID from any channel (Telegram, Discord, etc.).
*/
function getOnboardingFlow(userId) {
	let flow = flows.get(userId);
	if (!flow) {
		if (flows.size >= MAX_FLOWS) {
			for (const [id, f] of flows) if (!f.isActive) flows.delete(id);
			if (flows.size >= MAX_FLOWS) {
				const toDelete = flows.size - MAX_FLOWS + 50;
				let deleted = 0;
				for (const id of flows.keys()) {
					if (deleted >= toDelete) break;
					flows.delete(id);
					deleted++;
				}
			}
		}
		flow = new OnboardingFlow(userId);
		flows.set(userId, flow);
	}
	return flow;
}
/**
* Check if a user is new (no persisted state).
*/
function isNewUser(userId) {
	return loadState(userId) === null;
}
/**
* Clear in-memory flow cache. Used in tests.
*/
function resetOnboardingFlows() {
	flows.clear();
}
//#endregion
export { CAPABILITIES, OnboardingFlow, PERSONAS, getOnboardingFlow, isNewUser, loadState, resetOnboardingFlows, saveState };

//# sourceMappingURL=onboarding-flow.mjs.map