import { getWalletState, initWalletService } from "./src/services/walletconnect-service.mjs";
import { getCredentialVault } from "./src/services/credential-vault.mjs";
import { createClawnchConnectTool } from "./src/tools/clawnchconnect.mjs";
import { createDefiPriceTool } from "./src/tools/defi-price.mjs";
import { createDefiBalanceTool } from "./src/tools/defi-balance.mjs";
import { isReadonly } from "./src/services/mode-service.mjs";
import { createDefiSwapTool } from "./src/tools/defi-swap.mjs";
import { createClawnchLaunchTool } from "./src/tools/clawnch-launch.mjs";
import { createClawnchFeesTool } from "./src/tools/clawnch-fees.mjs";
import { createMarketIntelTool } from "./src/tools/market-intel.mjs";
import { createHummingbotTool } from "./src/tools/hummingbot.mjs";
import { createManageOrdersTool, persistOrders, restoreOrders } from "./src/tools/manage-orders.mjs";
import { createWatchActivityTool } from "./src/tools/watch-activity.mjs";
import { createClawnXTool } from "./src/tools/clawnx.mjs";
import { createHerdIntelligenceTool } from "./src/tools/herd-intelligence.mjs";
import { createCryptoWorkflowTool } from "./src/tools/crypto-workflow.mjs";
import { createTransferTool } from "./src/tools/transfer.mjs";
import { createLiquidityTool } from "./src/tools/liquidity.mjs";
import { createWayfinderTool } from "./src/tools/wayfinder.mjs";
import { createClawnchInfoTool } from "./src/tools/clawnch-info.mjs";
import { createPermit2Tool } from "./src/tools/permit2.mjs";
import { createCostBasisTool } from "./src/tools/cost-basis.mjs";
import { createAnalyticsTool } from "./src/tools/analytics.mjs";
import { createBlockExplorerTool } from "./src/tools/block-explorer.mjs";
import { createBridgeTool } from "./src/tools/bridge.mjs";
import { createMoltenTool } from "./src/tools/molten.mjs";
import { createBankrLaunchTool } from "./src/tools/bankr-launch.mjs";
import { createBankrAutomateTool } from "./src/tools/bankr-automate.mjs";
import { createBankrPolymarketTool } from "./src/tools/bankr-polymarket.mjs";
import { createBankrLeverageTool } from "./src/tools/bankr-leverage.mjs";
import { getScheduler } from "./src/services/plan-scheduler.mjs";
import { createCompoundActionTool } from "./src/tools/compound-action.mjs";
import { createDefiLendTool } from "./src/tools/defi-lend.mjs";
import { createApprovalsTool } from "./src/tools/approvals.mjs";
import { createDefiStakeTool } from "./src/tools/defi-stake.mjs";
import { createNftTool } from "./src/tools/nft.mjs";
import { createYieldTool } from "./src/tools/yield.mjs";
import { createGovernanceTool } from "./src/tools/governance.mjs";
import { createFarcasterTool } from "./src/tools/farcaster.mjs";
import { createSafeTool } from "./src/tools/safe.mjs";
import { createAirdropTool } from "./src/tools/airdrop.mjs";
import { createPrivacyTool } from "./src/tools/privacy.mjs";
import { createBrowserTool } from "./src/tools/browser.mjs";
import { createFiatPaymentTool } from "./src/tools/fiat-payment.mjs";
import { walletCommand } from "./src/commands/wallet-command.mjs";
import { policyCommand } from "./src/commands/policy-command.mjs";
import { txCommand } from "./src/commands/tx-command.mjs";
import { getOnboardingFlow } from "./src/services/onboarding-flow.mjs";
import { resetCommand, resetConfirmCommand } from "./src/commands/reset-command.mjs";
import { backCommand, capAllCommand, capCommands, chillCommand, createWalletCommand, degenCommand, importWalletCommand, mentorCommand, professionalCommand, restartCommand, skipCommand, technicalCommand } from "./src/commands/onboarding-commands.mjs";
import { autosignCommand, dangermodeCommand, modeCommand, readonlyCommand, safemodeCommand, walletsignCommand } from "./src/commands/mode-commands.mjs";
import { getHeartbeatMonitor } from "./src/services/heartbeat-monitor.mjs";
import { doctorCommand } from "./src/commands/doctor-command.mjs";
import { rpcCommand } from "./src/commands/rpc-command.mjs";
import { createChannelSender, extractChannelId, extractSenderId, parseSessionKey } from "./src/services/channel-sender.mjs";
import { connectBankrCommand, connectCommand, disconnectCommand, setConnectCommandApi, walletConnectCommands } from "./src/commands/connect-command.mjs";
import { llmShortcutCommands, modelCommand } from "./src/commands/model-command.mjs";
import { moltenCommand } from "./src/commands/molten-command.mjs";
import { automationsCommand, autotopupCommand, creditsCommand, topupCommand, usageCommand } from "./src/commands/bankr-commands.mjs";
import { flykeysCommand, flyrestartCommand, flystatusCommand, providerAnthropicCommand, providerBankrCommand, providerCommand, providerOpenaiCommand, providerOpenrouterCommand } from "./src/commands/fly-commands.mjs";
import { setupCommand } from "./src/commands/setup-command.mjs";
import { exportWalletCommand, recoverCommand, walletBackupCommand } from "./src/commands/wallet-manage-commands.mjs";
import { plansActiveCommand, plansCancelCommand, plansClearCommand, plansCommand } from "./src/commands/plans-command.mjs";
import { deadLetterCommand, triggersCommand, triggersCronCommand, triggersPriceCommand } from "./src/commands/trigger-commands.mjs";
import { createPendingConfirmation } from "./src/services/confirmation-store.mjs";
import { approveCommand, denyCommand } from "./src/commands/confirm-commands.mjs";
import { balanceCommand, chainCommand, helpCommand, portfolioCommand } from "./src/commands/help-command.mjs";
import { reportCommand, reportOptInCommand, reportOptOutCommand } from "./src/commands/report-command.mjs";
import { persistForumTopics, restoreForumTopics } from "./src/services/forum-topics.mjs";
import { persistThreadBindings, restoreThreadBindings } from "./src/services/thread-bindings.mjs";
import { getEvolutionMode } from "./src/services/evolution-mode.mjs";
import { getSessionRecall } from "./src/services/session-recall.mjs";
import { createAgentMemoryTool } from "./src/tools/agent-memory.mjs";
import { createSkillEvolveTool } from "./src/tools/skill-evolve.mjs";
import { createSessionRecallTool } from "./src/tools/session-recall.mjs";
import { evolutionCommand, evolveCommand, stableCommand } from "./src/commands/evolve-command.mjs";
import { topicBindCommand, topicUnbindCommand, topicsCommand, topicsSetupCommand } from "./src/commands/forum-commands.mjs";
import { fiatCommand } from "./src/commands/fiat-command.mjs";
import { toolsCommand } from "./src/commands/tools-command.mjs";
import { compileAllEnabledTools } from "./src/services/tool-compiler.mjs";
import { createAgentDelegateTool } from "./src/tools/agent-delegate.mjs";
import { agentsCommand } from "./src/commands/agents-command.mjs";
import { webhooksCommand } from "./src/commands/webhooks-command.mjs";
import { skillsCommand } from "./src/commands/skills-command.mjs";
import { getInterruptService } from "./src/services/interrupt-service.mjs";
import { interruptCommand, interruptPlanCommand } from "./src/commands/interrupt-command.mjs";
import { apiCommand } from "./src/commands/api-command.mjs";
import { usageNewCommand } from "./src/commands/usage-command.mjs";
import { restartCommand as restartCommand$1, setUpdateCommandSender, updateCommand } from "./src/commands/update-command.mjs";
import { pullCommand } from "./src/commands/pull-command.mjs";
import { evaluatePolicies, extractActionContext, extractPolicyUserId, getPolicyConfirmationStore, recordToolExecution } from "./src/services/policy-evaluator.mjs";
import { createPolicyManageTool } from "./src/tools/policy-manage.mjs";
import { policiesCommand } from "./src/commands/policies-command.mjs";
import { delegateCommand } from "./src/commands/delegate-command.mjs";
import { policymodeCommand } from "./src/commands/policymode-command.mjs";
import { profileCommand } from "./src/commands/profile-command.mjs";
import { upgradeCommand } from "./src/commands/upgrade-command.mjs";
import { agentAccountCommand } from "./src/commands/agent-account-command.mjs";
import { commandsAllCommand } from "./src/commands/commands-list.mjs";
import { tryDelegationExecution } from "./src/services/delegation-executor.mjs";
import { getTypingIndicator } from "./src/services/typing-indicator.mjs";
import { buildPromptContext } from "./src/hooks/prompt-builder.mjs";
import { handleAfterToolCall } from "./src/hooks/after-tool-call.mjs";
//#region extensions/crypto/index.ts
/**
* OpenClaw Plugin Definition
* 
* The `api` parameter is typed via `OpenClawPluginApi` from the plugin SDK.
* This gives compile-time safety against upstream API changes.
*/
const plugin = {
	id: "crypto",
	name: "Crypto DeFi Tools",
	description: "ClawnchConnect wallet, DeFi trading, token launchpad, and market intelligence",
	version: "0.1.0",
	register(api) {
		const WRITE_TOOL_NAMES = new Set([
			"defi_swap",
			"transfer",
			"bridge",
			"permit2",
			"clawnch_launch",
			"clawnch_fees",
			"liquidity",
			"compound_action",
			"manage_orders",
			"bankr_launch",
			"bankr_automate",
			"bankr_polymarket",
			"bankr_leverage",
			"clawnchconnect",
			"molten",
			"hummingbot",
			"clawnx",
			"defi_lend",
			"approvals",
			"defi_stake",
			"nft",
			"privacy",
			"yield",
			"browser",
			"governance",
			"farcaster",
			"safe",
			"airdrop",
			"fiat_payment",
			"wayfinder",
			"clawnch_info",
			"crypto_workflow"
		]);
		/**
		* Wrap a tool with a hard readonly gate + policy enforcement gate.
		* If the user is in readonly mode, write tools return an error.
		* If active policies block the action, it returns an error.
		* If policies require confirmation, it returns a confirmation request.
		* This is defense-in-depth — the LLM can't bypass by ignoring instructions.
		*/
		function registerToolWithReadonlyGate(tool) {
			if (WRITE_TOOL_NAMES.has(tool.name)) {
				const originalExecute = tool.execute;
				tool.execute = async (toolCallId, args, ctx) => {
					const userId = extractPolicyUserId(ctx);
					if (isReadonly(userId)) return {
						content: [{
							type: "text",
							text: `BLOCKED: Read-only mode is active. The tool "${tool.name}" writes to the blockchain and cannot be used in readonly mode. Use /safemode or /dangermode to re-enable write operations.`
						}],
						isError: true
					};
					const argsObj = args ?? {};
					const actionCtx = extractActionContext(tool.name, argsObj, userId);
					const decision = evaluatePolicies(actionCtx);
					if (decision.action === "block") return {
						content: [{
							type: "text",
							text: `BLOCKED by policy: ${decision.reason ?? "Policy violation."}`
						}],
						details: {
							blocked: true,
							policy: decision.policyName,
							rule: decision.ruleSummary
						},
						isError: true
					};
					if (decision.action === "confirm") {
						const nonce = argsObj.policyConfirmationNonce;
						if (nonce) {
							if (getPolicyConfirmationStore().consume(nonce, userId, tool.name)) {
								const delegationResult = await tryDelegationExecution(actionCtx, argsObj);
								if (delegationResult.executed) {
									try {
										recordToolExecution(actionCtx);
									} catch {}
									return {
										content: [{
											type: "text",
											text: `Executed via on-chain delegation. Tx: ${delegationResult.txHash} (chain ${delegationResult.chainId})`
										}],
										details: {
											delegationExecution: true,
											txHash: delegationResult.txHash,
											chainId: delegationResult.chainId
										}
									};
								}
								if (delegationResult.skipReason || delegationResult.error) console.info(`[delegation] Bypassed for ${tool.name}: ${delegationResult.skipReason ?? delegationResult.error}`);
								const result = await originalExecute.call(tool, toolCallId, args, ctx);
								if (!result?.isError) try {
									recordToolExecution(actionCtx);
								} catch {}
								return result;
							}
						}
						const newNonce = getPolicyConfirmationStore().create(userId, tool.name);
						return {
							content: [{
								type: "text",
								text: `POLICY HOLD: ${decision.reason ?? "Confirmation required."}\n\nAsk the user to confirm. Once confirmed, retry this tool call with the parameter policyConfirmationNonce="${newNonce}".`
							}],
							details: {
								needsConfirmation: true,
								policy: decision.policyName,
								rule: decision.ruleSummary,
								confirmationNonce: newNonce
							},
							isError: true
						};
					}
					const delegationResult = await tryDelegationExecution(actionCtx, argsObj);
					if (delegationResult.executed) {
						try {
							recordToolExecution(actionCtx);
						} catch {}
						return {
							content: [{
								type: "text",
								text: `Executed via on-chain delegation. Tx: ${delegationResult.txHash} (chain ${delegationResult.chainId})`
							}],
							details: {
								delegationExecution: true,
								txHash: delegationResult.txHash,
								chainId: delegationResult.chainId
							}
						};
					}
					if (delegationResult.skipReason || delegationResult.error) console.info(`[delegation] Bypassed for ${tool.name}: ${delegationResult.skipReason ?? delegationResult.error}`);
					const result = await originalExecute.call(tool, toolCallId, args, ctx);
					if (!result?.isError) try {
						recordToolExecution(actionCtx);
					} catch {}
					return result;
				};
			}
			api.registerTool(tool);
		}
		registerToolWithReadonlyGate(createClawnchConnectTool(api));
		registerToolWithReadonlyGate(createDefiPriceTool());
		registerToolWithReadonlyGate(createDefiBalanceTool());
		registerToolWithReadonlyGate(createDefiSwapTool());
		registerToolWithReadonlyGate(createClawnchLaunchTool());
		registerToolWithReadonlyGate(createClawnchFeesTool());
		registerToolWithReadonlyGate(createMarketIntelTool());
		registerToolWithReadonlyGate(createHummingbotTool());
		registerToolWithReadonlyGate(createManageOrdersTool());
		registerToolWithReadonlyGate(createWatchActivityTool());
		registerToolWithReadonlyGate(createClawnXTool());
		registerToolWithReadonlyGate(createHerdIntelligenceTool());
		registerToolWithReadonlyGate(createCryptoWorkflowTool());
		registerToolWithReadonlyGate(createTransferTool());
		registerToolWithReadonlyGate(createLiquidityTool());
		registerToolWithReadonlyGate(createWayfinderTool());
		registerToolWithReadonlyGate(createClawnchInfoTool());
		registerToolWithReadonlyGate(createPermit2Tool());
		registerToolWithReadonlyGate(createCostBasisTool());
		registerToolWithReadonlyGate(createAnalyticsTool());
		registerToolWithReadonlyGate(createBlockExplorerTool());
		registerToolWithReadonlyGate(createBridgeTool());
		registerToolWithReadonlyGate(createMoltenTool());
		registerToolWithReadonlyGate(createBankrLaunchTool());
		registerToolWithReadonlyGate(createBankrAutomateTool());
		registerToolWithReadonlyGate(createBankrPolymarketTool());
		registerToolWithReadonlyGate(createBankrLeverageTool());
		registerToolWithReadonlyGate(createCompoundActionTool());
		registerToolWithReadonlyGate(createDefiLendTool());
		registerToolWithReadonlyGate(createApprovalsTool());
		registerToolWithReadonlyGate(createDefiStakeTool());
		registerToolWithReadonlyGate(createNftTool());
		registerToolWithReadonlyGate(createYieldTool());
		registerToolWithReadonlyGate(createGovernanceTool());
		registerToolWithReadonlyGate(createFarcasterTool());
		registerToolWithReadonlyGate(createSafeTool());
		registerToolWithReadonlyGate(createAirdropTool());
		registerToolWithReadonlyGate(createPrivacyTool());
		registerToolWithReadonlyGate(createBrowserTool());
		registerToolWithReadonlyGate(createFiatPaymentTool());
		let agentDispatcher = { call: async () => {
			throw new Error("Agent dispatcher not ready");
		} };
		let registeredToolsList = [];
		registerToolWithReadonlyGate(createAgentDelegateTool(() => agentDispatcher, () => registeredToolsList));
		{
			const memoryTool = createAgentMemoryTool();
			const skillTool = createSkillEvolveTool();
			const recallTool = createSessionRecallTool();
			const WRITE_ACTIONS_MEMORY = new Set([
				"add",
				"replace",
				"remove",
				"user_add",
				"user_remove"
			]);
			const WRITE_ACTIONS_SKILL = new Set([
				"create",
				"patch",
				"delete"
			]);
			const wrapWithEvoGate = (tool, writeActions) => {
				const originalExecute = tool.execute;
				tool.execute = async (toolCallId, args, ctx) => {
					const action = args?.action ?? "";
					if (writeActions.has(action)) {
						const userId = ctx?.senderId ?? ctx?.from ?? ctx?.metadata?.senderId;
						if (userId && !getEvolutionMode().isEvolving(userId)) return {
							content: [{
								type: "text",
								text: `Self-improvement is in stable mode. The "${action}" action requires evolving mode. Use /evolve to enable self-improvement.`
							}],
							isError: true
						};
					}
					return originalExecute.call(tool, toolCallId, args, ctx);
				};
				return tool;
			};
			api.registerTool(wrapWithEvoGate(memoryTool, WRITE_ACTIONS_MEMORY));
			api.registerTool(wrapWithEvoGate(skillTool, WRITE_ACTIONS_SKILL));
			api.registerTool(recallTool);
		}
		api.registerTool(createPolicyManageTool());
		api.registerCommand(walletCommand);
		api.registerCommand(policyCommand);
		api.registerCommand(txCommand);
		api.registerCommand(resetCommand);
		api.registerCommand(resetConfirmCommand);
		api.registerCommand(professionalCommand);
		api.registerCommand(degenCommand);
		api.registerCommand(chillCommand);
		api.registerCommand(technicalCommand);
		api.registerCommand(mentorCommand);
		api.registerCommand(capAllCommand);
		for (const cmd of capCommands) api.registerCommand(cmd);
		api.registerCommand(skipCommand);
		api.registerCommand(backCommand);
		api.registerCommand(restartCommand);
		api.registerCommand(createWalletCommand);
		api.registerCommand(importWalletCommand);
		api.registerCommand(recoverCommand);
		api.registerCommand(exportWalletCommand);
		api.registerCommand(walletBackupCommand);
		api.registerCommand(safemodeCommand);
		api.registerCommand(dangermodeCommand);
		api.registerCommand(readonlyCommand);
		api.registerCommand(walletsignCommand);
		api.registerCommand(autosignCommand);
		api.registerCommand(modeCommand);
		setConnectCommandApi(api);
		api.registerCommand(connectCommand);
		for (const cmd of walletConnectCommands) api.registerCommand(cmd);
		api.registerCommand(disconnectCommand);
		api.registerCommand(modelCommand);
		for (const cmd of llmShortcutCommands) api.registerCommand(cmd);
		api.registerCommand(moltenCommand);
		api.registerCommand(creditsCommand);
		api.registerCommand(usageCommand);
		api.registerCommand(connectBankrCommand);
		api.registerCommand(automationsCommand);
		api.registerCommand(topupCommand);
		api.registerCommand(autotopupCommand);
		api.registerCommand(providerCommand);
		api.registerCommand(providerAnthropicCommand);
		api.registerCommand(providerBankrCommand);
		api.registerCommand(providerOpenrouterCommand);
		api.registerCommand(providerOpenaiCommand);
		api.registerCommand(flykeysCommand);
		api.registerCommand(flystatusCommand);
		api.registerCommand(flyrestartCommand);
		api.registerCommand(setupCommand);
		api.registerCommand(plansCommand);
		api.registerCommand(plansActiveCommand);
		api.registerCommand(plansCancelCommand);
		api.registerCommand(plansClearCommand);
		api.registerCommand(approveCommand);
		api.registerCommand(denyCommand);
		api.registerCommand(triggersCommand);
		api.registerCommand(triggersPriceCommand);
		api.registerCommand(triggersCronCommand);
		api.registerCommand(deadLetterCommand);
		api.registerCommand(helpCommand);
		api.registerCommand(portfolioCommand);
		api.registerCommand(balanceCommand);
		api.registerCommand(chainCommand);
		api.registerCommand(doctorCommand);
		api.registerCommand(rpcCommand);
		api.registerCommand(evolveCommand);
		api.registerCommand(stableCommand);
		api.registerCommand(evolutionCommand);
		api.registerCommand(topicsCommand);
		api.registerCommand(topicsSetupCommand);
		api.registerCommand(topicBindCommand);
		api.registerCommand(topicUnbindCommand);
		api.registerCommand(fiatCommand);
		api.registerCommand(toolsCommand);
		api.registerCommand(agentsCommand);
		api.registerCommand(webhooksCommand);
		api.registerCommand(skillsCommand);
		api.registerCommand(interruptCommand);
		api.registerCommand(interruptPlanCommand);
		api.registerCommand(reportCommand);
		api.registerCommand(reportOptInCommand);
		api.registerCommand(reportOptOutCommand);
		api.registerCommand(apiCommand);
		api.registerCommand(usageNewCommand);
		api.registerCommand(updateCommand);
		api.registerCommand(restartCommand$1);
		api.registerCommand(pullCommand);
		api.registerCommand(policiesCommand);
		api.registerCommand(delegateCommand);
		api.registerCommand(policymodeCommand);
		api.registerCommand(profileCommand);
		api.registerCommand(upgradeCommand);
		api.registerCommand(agentAccountCommand);
		api.registerCommand(commandsAllCommand);
		api.on("gateway_start", async () => {
			try {
				const { hydrateApiKeys } = await import("./src/services/keychain-secrets.mjs");
				const { loaded, skipped } = hydrateApiKeys();
				if (loaded.length > 0) api.logger?.info?.(`[crypto] Loaded API keys from Keychain: ${loaded.join(", ")}`);
				if (skipped.length > 0) api.logger?.info?.(`[crypto] API keys already in env (skipped Keychain): ${skipped.join(", ")}`);
			} catch (err) {
				api.logger?.warn?.(`[crypto] Failed to hydrate API keys from Keychain: ${err instanceof Error ? err.message : String(err)}`);
			}
			const projectId = process.env.WALLETCONNECT_PROJECT_ID;
			const privateKey = process.env.CLAWNCHER_PRIVATE_KEY;
			const bankrApiKey = process.env.BANKR_API_KEY;
			if (!projectId && !privateKey && !bankrApiKey) api.logger?.info?.("[crypto] No wallet configured. Set WALLETCONNECT_PROJECT_ID, CLAWNCHER_PRIVATE_KEY, or BANKR_API_KEY to enable write operations.");
			else if (privateKey) if (process.env.ALLOW_PRIVATE_KEY_MODE !== "true") api.logger?.warn?.("[crypto] CLAWNCHER_PRIVATE_KEY is set but ALLOW_PRIVATE_KEY_MODE is not \"true\". Private key mode is disabled for safety. Set ALLOW_PRIVATE_KEY_MODE=true to enable.");
			else try {
				const result = await initWalletService({
					privateKey,
					rpcUrl: process.env.CLAWNCHER_RPC_URL,
					network: process.env.CLAWNCHER_NETWORK || "mainnet"
				});
				api.logger?.warn?.(`[crypto] WARNING: Wallet running in PRIVATE KEY mode (auto-sign). Address: ${result.address}`);
			} catch (err) {
				api.logger?.warn?.(`[crypto] Private key wallet init failed: ${err instanceof Error ? err.message : String(err)}`);
			}
			if (projectId && !privateKey && !bankrApiKey) api.logger?.info?.("[crypto] WalletConnect available (project ID configured). Use the clawnchconnect tool to pair a wallet.");
			if (bankrApiKey && !privateKey) try {
				const result = await initWalletService({ bankrApiKey });
				if (result.mode === "bankr") api.logger?.info?.(`[crypto] Bankr wallet ready: ${result.address}${result.solAddress ? ` (+ Solana: ${result.solAddress})` : ""}`);
				else api.logger?.warn?.("[crypto] Bankr wallet init returned unexpected mode");
			} catch (err) {
				api.logger?.warn?.(`[crypto] Bankr wallet init failed: ${err instanceof Error ? err.message : String(err)}`);
			}
			try {
				restoreForumTopics();
				restoreThreadBindings();
				restoreOrders();
				api.logger?.info?.("[crypto] Restored persisted state (forum topics, thread bindings, orders)");
			} catch (err) {
				api.logger?.warn?.(`[crypto] Failed to restore some persisted state: ${err instanceof Error ? err.message : String(err)}`);
			}
			try {
				const runtimeRef = api.runtime;
				const compiledUserTools = compileAllEnabledTools({ call: async (toolName, args) => {
					const tool = (runtimeRef?.tools?.getAll?.() ?? []).find((t) => t.name === toolName);
					if (!tool) throw new Error(`Tool "${toolName}" not found in registry`);
					const toolCallId = `user-tool-${Date.now()}-${toolName}`;
					return tool.execute(toolCallId, args, {});
				} });
				for (const userTool of compiledUserTools) registerToolWithReadonlyGate(userTool);
				if (compiledUserTools.length > 0) api.logger?.info?.(`[crypto] Registered ${compiledUserTools.length} user-defined tool(s): ${compiledUserTools.map((t) => t.name).join(", ")}`);
			} catch (err) {
				api.logger?.warn?.(`[crypto] Failed to load user-defined tools: ${err instanceof Error ? err.message : String(err)}`);
			}
			try {
				const runtimeForAgents = api.runtime;
				agentDispatcher = { call: async (toolName, args) => {
					const tool = (runtimeForAgents?.tools?.getAll?.() ?? []).find((t) => t.name === toolName);
					if (!tool) throw new Error(`Tool "${toolName}" not found in registry`);
					const toolCallId = `agent-${Date.now()}-${toolName}`;
					return tool.execute(toolCallId, args, {});
				} };
				registeredToolsList = (runtimeForAgents?.tools?.getAll?.() ?? []).map((t) => ({
					name: t.name,
					description: t.description ?? "",
					parameters: t.parameters ?? {
						type: "object",
						properties: {}
					}
				}));
				api.logger?.info?.(`[crypto] Agent delegate ready: ${registeredToolsList.length} tools available for sub-agents`);
			} catch (err) {
				api.logger?.warn?.(`[crypto] Agent dispatcher setup failed: ${err instanceof Error ? err.message : String(err)}`);
			}
			try {
				const { getWebhookServer } = await import("./src/services/webhook-server.mjs");
				const { getEventBus } = await import("./src/services/event-bus.mjs");
				const webhookServer = getWebhookServer();
				webhookServer.onEvent(async (event) => {
					getEventBus().emit("webhook_received", {
						type: "webhook_received",
						route: event.route,
						source: event.source,
						payload: event.payload,
						headers: event.headers,
						receivedAt: event.receivedAt,
						timestamp: Date.now()
					});
					api.logger?.info?.(`[crypto] Webhook received: ${event.route} from ${event.source}`);
				});
				if (await webhookServer.start()) {
					const config = webhookServer.getConfig();
					api.logger?.info?.(`[crypto] Webhook server listening on ${config.host}:${config.port}`);
				}
			} catch (err) {
				api.logger?.warn?.(`[crypto] Webhook server failed to start: ${err instanceof Error ? err.message : String(err)}`);
			}
			try {
				const { getScheduler } = await import("./src/services/plan-scheduler.mjs");
				const { PlanExecutor, formatExecutionSummary } = await import("./src/services/plan-executor.mjs");
				const { getPrice } = await import("./src/services/price-service.mjs");
				const { getGasEstimator } = await import("./src/services/gas-estimator.mjs");
				const { getRpcManager } = await import("./src/services/rpc-provider.mjs");
				const scheduler = getScheduler({ resolver: {
					price: async (token) => {
						try {
							const data = await getPrice(token);
							if (!data?.priceUsd) return NaN;
							return data.priceUsd;
						} catch (err) {
							api.logger?.warn?.(`[plan-resolver] price fetch failed for ${token}: ${err}`);
							return NaN;
						}
					},
					balance: async (token, chainId) => {
						try {
							const cid = chainId ?? 8453;
							const client = await getRpcManager().getClient(cid);
							const walletState = getWalletState();
							if (!walletState.address) return NaN;
							if (!token || token.toUpperCase() === "ETH") {
								const balance = await client.getBalance({ address: walletState.address });
								return Number(balance) / 0xde0b6b3a7640000;
							}
							const { resolveTokenDecimals } = await import("./src/lib/token-decimals.mjs");
							const decimals = await resolveTokenDecimals(token, client);
							const data = await client.readContract({
								address: token,
								abi: [{
									name: "balanceOf",
									type: "function",
									stateMutability: "view",
									inputs: [{
										name: "account",
										type: "address"
									}],
									outputs: [{
										name: "",
										type: "uint256"
									}]
								}],
								functionName: "balanceOf",
								args: [walletState.address]
							});
							return Number(data) / 10 ** decimals;
						} catch (err) {
							api.logger?.warn?.(`[plan-resolver] balance fetch failed for ${token} on chain ${chainId ?? 8453}: ${err}`);
							return NaN;
						}
					},
					gasPrice: async (chainId) => {
						try {
							return (await getGasEstimator().getGasPrice(chainId ?? 8453)).totalStandard;
						} catch (err) {
							api.logger?.warn?.(`[plan-resolver] gas price fetch failed for chain ${chainId ?? 8453}: ${err}`);
							return NaN;
						}
					},
					timestamp: () => Math.floor(Date.now() / 1e3),
					blockNumber: async (chainId) => {
						try {
							const block = await (await getRpcManager().getClient(chainId ?? 8453)).getBlockNumber();
							return Number(block);
						} catch (err) {
							api.logger?.warn?.(`[plan-resolver] block number fetch failed for chain ${chainId ?? 8453}: ${err}`);
							return NaN;
						}
					}
				} });
				const runtimeInternal = api.runtime;
				const executor = new PlanExecutor({
					dispatcher: {
						call: async (toolName, params, userId) => {
							const tool = (runtimeInternal?.tools?.getAll?.() ?? []).find((t) => t.name === toolName);
							if (!tool) throw new Error(`Tool "${toolName}" not found in registry`);
							const toolCallId = `plan-${Date.now()}-${toolName}`;
							const ctx = userId ? { senderId: userId } : {};
							const result = await tool.execute(toolCallId, params, ctx);
							if (result && typeof result === "object") {
								const r = result;
								return r.details ?? r.text ?? result;
							}
							return result;
						},
						exists: (toolName) => {
							return (runtimeInternal?.tools?.getAll?.() ?? []).some((t) => t.name === toolName);
						}
					},
					scheduler,
					onConfirmRequired: async (step, resolvedParams, planUserId) => {
						if (planUserId && planUserId !== "owner") try {
							const parsed = parseSessionKey(planUserId);
							const paramLines = Object.entries(resolvedParams).filter(([, v]) => v !== void 0 && v !== null).slice(0, 6).map(([k, v]) => `  ${k}: ${String(v)}`).join("\n");
							const msg = `**Confirmation required**\n\nStep: **${step.label}**\nTool: ${step.tool}\n` + (paramLines ? `Params:\n${paramLines}\n\n` : "\n") + `Reply /approve to continue or /deny to skip.`;
							if (parsed) await sender.send(parsed.channel, parsed.userId, msg);
							else await sender.send("telegram", planUserId, msg);
						} catch {}
						return createPendingConfirmation({
							executionId: `${step.id}-${Date.now()}`,
							planName: step.label,
							stepLabel: step.label,
							tool: step.tool,
							params: resolvedParams,
							userId: planUserId
						});
					},
					onDeadLetter: (entry) => {
						scheduler.saveDeadLetter(entry);
						api.logger?.warn?.(`[crypto] Dead-letter: plan=${entry.planId} node=${entry.nodeId} error=${entry.error}`);
						if (entry.userId && entry.userId !== "owner") {
							const parsed = parseSessionKey(entry.userId);
							const msg = `**Plan step failed permanently**\nPlan: \`${entry.planId}\`\nStep: \`${entry.nodeId}\`${entry.tool ? ` (${entry.tool})` : ""}\nError: ${entry.error}\nRetries: ${entry.retryCount}\nUse \`/plans dead_letter\` to view all failures.`;
							try {
								if (parsed) sender.send(parsed.channel, parsed.userId, msg).catch(() => {});
								else sender.send("telegram", entry.userId, msg).catch(() => {});
							} catch {}
						}
					}
				});
				const sender = createChannelSender(api);
				scheduler.on(async (event) => {
					if (event.type === "trigger_fired") {
						const plan = event.plan;
						api.logger?.info?.(`[crypto] Plan trigger fired: ${plan.name} (${plan.id})`);
						const planUserId = plan.userId;
						if (planUserId && planUserId !== "owner") try {
							const parsed = parseSessionKey(planUserId);
							if (parsed) await sender.send(parsed.channel, parsed.userId, `**Plan executing:** ${plan.name}\nTrigger fired — running steps now...`);
							else await sender.send("telegram", planUserId, `**Plan executing:** ${plan.name}\nTrigger fired — running steps now...`);
						} catch {}
						try {
							const execution = await executor.execute(plan, event.executionId);
							const summary = formatExecutionSummary(execution, plan);
							api.logger?.info?.(`[crypto] Plan execution complete: ${plan.name} — ${execution.status}`);
							if (planUserId && planUserId !== "owner") try {
								const parsed = parseSessionKey(planUserId);
								if (parsed) await sender.send(parsed.channel, parsed.userId, summary);
								else await sender.send("telegram", planUserId, summary);
							} catch {}
						} catch (execErr) {
							api.logger?.warn?.(`[crypto] Plan execution failed: ${plan.name} — ${execErr instanceof Error ? execErr.message : String(execErr)}`);
						}
					} else if (event.type === "plan_expired") {
						api.logger?.info?.(`[crypto] Plan expired: ${event.plan.name} — ${event.reason}`);
						const planUserId = event.plan.userId;
						if (planUserId && planUserId !== "owner") try {
							const parsed = parseSessionKey(planUserId);
							if (parsed) await sender.send(parsed.channel, parsed.userId, `**Plan expired:** ${event.plan.name}\nReason: ${event.reason}`);
							else await sender.send("telegram", planUserId, `**Plan expired:** ${event.plan.name}\nReason: ${event.reason}`);
						} catch {}
					} else if (event.type === "condition_check_error") api.logger?.warn?.(`[crypto] Condition check error for plan ${event.planId}: ${event.error}`);
				});
				scheduler.start();
				api.logger?.info?.(`[crypto] Plan scheduler started (${scheduler.activeCount} active plans)`);
				try {
					const { getPriceWatcher } = await import("./src/services/price-watcher.mjs");
					const { getEventBus } = await import("./src/services/event-bus.mjs");
					const priceWatcher = getPriceWatcher();
					const eventBus = getEventBus();
					const activePlans = scheduler.getActivePlans();
					let priceWatchCount = 0;
					for (const plan of activePlans) if (plan.trigger?.type === "price") {
						priceWatcher.addFromTrigger(plan.id, plan.trigger);
						priceWatchCount++;
					}
					eventBus.on("price_crossed", async (event) => {
						for (const watch of priceWatcher.getWatches()) if (watch.token.toUpperCase() === event.token.toUpperCase() && watch.condition === event.condition && watch.threshold === event.threshold) try {
							await scheduler.firePriceTrigger(watch.id);
							api.logger?.info?.(`[crypto] Price trigger fired: plan=${watch.id} token=${event.token} price=$${event.currentPrice.toFixed(2)} condition=${event.condition} threshold=$${event.threshold}`);
						} catch (err) {
							api.logger?.warn?.(`[crypto] Failed to fire price trigger for plan ${watch.id}: ${err instanceof Error ? err.message : String(err)}`);
						}
					});
					scheduler.on(async (event) => {
						if (event.type === "plan_added" && event.plan?.trigger?.type === "price") {
							priceWatcher.addFromTrigger(event.plan.id, event.plan.trigger);
							if (!priceWatcher.isRunning && priceWatcher.watchCount > 0) {
								priceWatcher.start();
								api.logger?.info?.(`[crypto] Price watcher auto-started for plan ${event.plan.id}`);
							}
						} else if (event.type === "plan_cancelled") {
							priceWatcher.removeWatch(event.planId);
							if (priceWatcher.isRunning && priceWatcher.watchCount === 0) {
								priceWatcher.stop();
								api.logger?.info?.("[crypto] Price watcher stopped (no active watches)");
							}
						}
					});
					if (priceWatchCount > 0) {
						priceWatcher.start();
						api.logger?.info?.(`[crypto] Price watcher started (${priceWatchCount} active watches)`);
					} else api.logger?.info?.("[crypto] Price watcher ready (no active price triggers — will start on first price-triggered plan)");
				} catch (err) {
					api.logger?.warn?.(`[crypto] Price watcher/event bus failed to start: ${err instanceof Error ? err.message : String(err)}`);
				}
				api.registerService?.({
					id: "crypto-plan-scheduler",
					start: () => {},
					stop: () => {
						scheduler.stop();
						import("./src/services/price-watcher.mjs").then(({ getPriceWatcher }) => getPriceWatcher().stop()).catch(() => {});
					}
				});
			} catch (err) {
				api.logger?.warn?.(`[crypto] Plan scheduler failed to start: ${err instanceof Error ? err.message : String(err)}`);
			}
			try {
				const heartbeat = getHeartbeatMonitor({
					intervalMs: parseInt(process.env.OPENCLAWNCH_HEARTBEAT_INTERVAL_MS ?? "300000", 10),
					priceDropAlertPercent: parseFloat(process.env.OPENCLAWNCH_HEARTBEAT_DROP_PCT ?? "10"),
					priceGainAlertPercent: parseFloat(process.env.OPENCLAWNCH_HEARTBEAT_GAIN_PCT ?? "20"),
					portfolioDropAlertUsd: parseFloat(process.env.OPENCLAWNCH_HEARTBEAT_DROP_USD ?? "100"),
					enabled: process.env.OPENCLAWNCH_HEARTBEAT_ENABLED !== "false"
				});
				const hbSender = createChannelSender(api);
				const ownerChatId = process.env.OPENCLAWNCH_OWNER_CHAT_ID ?? api.config?.channels?.telegram?.allowFrom?.[0] ?? null;
				heartbeat.onAlert(async (alert) => {
					if (!ownerChatId) return;
					const message = `${alert.severity === "critical" ? "**CRITICAL**" : alert.severity === "warning" ? "**WARNING**" : "INFO"} [Heartbeat] ${alert.message}`;
					const availableChannels = hbSender.availableChannels();
					for (const ch of availableChannels) try {
						await hbSender.send(ch, ownerChatId, message);
						break;
					} catch {}
				});
				heartbeat.start();
				api.logger?.info?.("[crypto] Heartbeat position monitor started");
				api.registerService?.({
					id: "crypto-heartbeat-monitor",
					start: () => {},
					stop: () => {
						heartbeat.stop();
					}
				});
			} catch (err) {
				api.logger?.warn?.(`[crypto] Heartbeat monitor failed to start: ${err instanceof Error ? err.message : String(err)}`);
			}
			if (process.env.BANKR_LLM_KEY) try {
				const { getCreditMonitor } = await import("./src/services/credit-monitor.mjs");
				const creditMonitor = getCreditMonitor({
					intervalMs: parseInt(process.env.OPENCLAWNCH_CREDIT_CHECK_INTERVAL_MS ?? "300000", 10),
					warningThresholdUsd: parseFloat(process.env.OPENCLAWNCH_CREDIT_WARNING_USD ?? "5"),
					criticalThresholdUsd: parseFloat(process.env.OPENCLAWNCH_CREDIT_CRITICAL_USD ?? "1")
				});
				const creditSender = createChannelSender(api);
				const creditOwnerChatId = process.env.OPENCLAWNCH_OWNER_CHAT_ID ?? api.config?.channels?.telegram?.allowFrom?.[0] ?? null;
				creditMonitor.onAlert(async (alert) => {
					if (!creditOwnerChatId) return;
					const message = `${alert.severity === "critical" ? "**CRITICAL**" : "**WARNING**"} ${alert.message}`;
					const availableChannels = creditSender.availableChannels();
					for (const ch of availableChannels) try {
						await creditSender.send(ch, creditOwnerChatId, message);
						break;
					} catch {}
				});
				creditMonitor.start();
				api.logger?.info?.("[crypto] LLM credit monitor started");
				api.registerService?.({
					id: "crypto-credit-monitor",
					start: () => {},
					stop: () => {
						creditMonitor.stop();
					}
				});
			} catch (err) {
				api.logger?.warn?.(`[crypto] Credit monitor failed to start: ${err instanceof Error ? err.message : String(err)}`);
			}
			const shutdownServices = () => {
				try {
					getScheduler().stop();
				} catch {}
				try {
					getHeartbeatMonitor().stop();
				} catch {}
				try {
					import("./src/services/credit-monitor.mjs").then(({ getCreditMonitor }) => getCreditMonitor().stop()).catch(() => {});
				} catch {}
				try {
					persistForumTopics();
				} catch {}
				try {
					persistThreadBindings();
				} catch {}
				try {
					persistOrders();
				} catch {}
				api.logger?.info?.("[crypto] Graceful shutdown: services stopped, state persisted");
			};
			let shutdownCalled = false;
			const onShutdownSignal = (signal) => {
				if (shutdownCalled) return;
				shutdownCalled = true;
				api.logger?.info?.(`[crypto] Received ${signal}, shutting down...`);
				shutdownServices();
			};
			process.on("SIGTERM", () => onShutdownSignal("SIGTERM"));
			process.on("SIGINT", () => onShutdownSignal("SIGINT"));
			process.on("beforeExit", () => {
				if (!shutdownCalled) {
					shutdownCalled = true;
					shutdownServices();
				}
			});
		});
		const ONBOARDING_FLAG_TTL_MS = 3e4;
		const onboardingHandledConversations = /* @__PURE__ */ new Map();
		/** Add a chatId flag with a TTL. */
		function markConversationHandled(chatId) {
			onboardingHandledConversations.set(chatId, Date.now());
		}
		/** Check and consume a flag, returning true if valid (not expired). */
		function consumeConversationFlag(chatId) {
			const ts = onboardingHandledConversations.get(chatId);
			if (ts == null) return false;
			onboardingHandledConversations.delete(chatId);
			if (Date.now() - ts > ONBOARDING_FLAG_TTL_MS) return false;
			return true;
		}
		setInterval(() => {
			const now = Date.now();
			for (const [key, ts] of onboardingHandledConversations) if (now - ts > ONBOARDING_FLAG_TTL_MS) onboardingHandledConversations.delete(key);
		}, 6e4).unref();
		const channelSender = createChannelSender(api);
		setUpdateCommandSender(channelSender);
		/** Send an onboarding message to a chat on the given channel. */
		async function sendOnboardingMessage(channel, chatId, msg) {
			try {
				if (!await channelSender.send(channel, chatId, msg.text)) api.logger?.warn?.(`[crypto] sendMessage for ${channel} not available on runtime`);
			} catch (err) {
				api.logger?.warn?.(`[crypto] Failed to send onboarding message via ${channel}: ${err instanceof Error ? err.message : String(err)}`);
			}
		}
		api.on("message_received", async (event, ctx) => {
			try {
				const channel = extractChannelId(ctx);
				if (!channel) return;
				const userId = extractSenderId(event, ctx);
				if (!userId) return;
				const chatId = ctx?.conversationId ?? String(userId);
				if (channel === "telegram") try {
					getTypingIndicator().start(String(chatId));
				} catch {}
				const message = event?.content ?? "";
				const msgStr = String(message);
				if (msgStr.startsWith("/") && msgStr !== "/start") return;
				const flow = getOnboardingFlow(String(userId));
				if (!flow.isActive) return;
				const response = await flow.processMessage(String(message));
				if (response) {
					markConversationHandled(chatId);
					await sendOnboardingMessage(channel, chatId, response);
					api.logger?.info?.(`[crypto] Onboarding step for user ${userId} on ${channel}: ${flow.currentStep}`);
				}
				try {
					const sessionKey = ctx?.sessionKey ?? `${channel}-${chatId}`;
					getSessionRecall().recordTurn({
						sessionKey,
						role: "user",
						content: String(message).slice(0, 2e3),
						userId: String(userId),
						timestamp: Date.now()
					});
				} catch {}
			} catch (err) {
				api.logger?.warn?.(`[crypto] Onboarding message hook error: ${err instanceof Error ? err.message : String(err)}`);
			}
		});
		api.on("message_sending", (event, ctx) => {
			try {
				const chatId = ctx?.conversationId ?? event?.to;
				if (!chatId) return;
				try {
					getTypingIndicator().stop(String(chatId));
				} catch {}
				if (consumeConversationFlag(String(chatId))) {
					api.logger?.info?.(`[crypto] Suppressing LLM response for onboarding chat ${chatId}`);
					return { cancel: true };
				}
				try {
					const sessionKey = ctx?.sessionKey ?? String(chatId);
					if (getInterruptService().consume(sessionKey)) {
						api.logger?.info?.(`[crypto] Response interrupted for session ${sessionKey}`);
						return { cancel: true };
					}
				} catch {}
				const content = event?.content ?? event?.text ?? "";
				if (typeof content === "string" && content.length > 0) {
					const scan = getCredentialVault().scanForLeaks(content);
					if (!scan.clean) {
						api.logger?.warn?.(`[crypto] Credential leak detected in outbound message! ${scan.leaks.length} leak(s): ${scan.leaks.map((l) => l.type).join(", ")}. Redacting.`);
						return { content: scan.redactedText };
					}
				}
			} catch (err) {
				api.logger?.warn?.(`[crypto] message_sending hook error: ${err instanceof Error ? err.message : String(err)}`);
			}
		});
		api.on("before_prompt_build", (event, ctx) => {
			return buildPromptContext(event, ctx, {
				getWalletState,
				logger: api.logger
			});
		});
		api.on("after_tool_call", async (event, ctx) => {
			await handleAfterToolCall(event, ctx, {
				writeToolNames: WRITE_TOOL_NAMES,
				sendOnboardingMessage,
				markOnboardingHandled: markConversationHandled,
				getWalletState,
				logger: api.logger
			});
		});
	}
};
//#endregion
export { plugin as default };

//# sourceMappingURL=index.mjs.map