agentlili 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +533 -0
- package/dist/chunk-AAYS2L5P.js +946 -0
- package/dist/chunk-AAYS2L5P.js.map +1 -0
- package/dist/cli.js +803 -0
- package/dist/cli.js.map +1 -0
- package/dist/mcp-server.js +1468 -0
- package/dist/mcp-server.js.map +1 -0
- package/package.json +111 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mcp/server.ts","../src/lib/audit-log.ts","../src/lib/spending-limits.ts","../src/lib/agent-lifecycle.ts","../src/lib/tx-log.ts","../src/lib/agent-strategy.ts","../src/lib/agent-monitor.ts","../src/lib/portfolio.ts","../src/lib/fraud-monitor.ts","../src/lib/notifications.ts","../src/lib/event-bus.ts","../src/mcp/tools.ts","../src/mcp/prompts.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * stng_defi_wallets MCP Server\n *\n * Exposes 18 DeFi, wallet, and agent management tools via the\n * Model Context Protocol (MCP) for use with Claude Desktop, Cursor,\n * and other MCP-compatible clients.\n *\n * Usage:\n * WALLET_ENCRYPTION_KEY=<hex> npx tsx src/mcp/server.ts\n *\n * Claude Desktop config (~/.claude/claude_desktop_config.json):\n * {\n * \"mcpServers\": {\n * \"stng-defi\": {\n * \"command\": \"npx\",\n * \"args\": [\"tsx\", \"src/mcp/server.ts\"],\n * \"cwd\": \"/path/to/stng_defi_wallets\",\n * \"env\": {\n * \"WALLET_ENCRYPTION_KEY\": \"<your-key>\",\n * \"SOLANA_RPC_URL\": \"https://api.devnet.solana.com\"\n * }\n * }\n * }\n * }\n */\n\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n ListPromptsRequestSchema,\n GetPromptRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { TOOLS, handleTool } from \"./tools.js\";\nimport { PROMPTS, getPromptContent } from \"./prompts.js\";\nimport type { WalletContext } from \"./types.js\";\n\n// ============================================\n// Server factory\n// ============================================\n\nfunction createServer(ctx: WalletContext): Server {\n const server = new Server(\n {\n name: \"stng-defi-wallets\",\n version: \"0.1.0\",\n },\n {\n capabilities: {\n tools: {},\n prompts: {},\n },\n },\n );\n\n // List available tools\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n return { tools: [...TOOLS] };\n });\n\n // List available prompts\n server.setRequestHandler(ListPromptsRequestSchema, async () => {\n return { prompts: PROMPTS };\n });\n\n // Get prompt content\n server.setRequestHandler(GetPromptRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n const prompt = PROMPTS.find((p) => p.name === name);\n if (!prompt) {\n throw new Error(`Unknown prompt: ${name}`);\n }\n\n const content = getPromptContent(name, args || {});\n\n return {\n description: prompt.description,\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: content,\n },\n },\n ],\n };\n });\n\n // Execute tools\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n try {\n const result = await handleTool(\n name,\n (args as Record<string, unknown>) ?? {},\n ctx,\n );\n\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"Unknown error\";\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify({ error: message }),\n },\n ],\n isError: true,\n };\n }\n });\n\n return server;\n}\n\n// ============================================\n// Entry point\n// ============================================\n\nexport async function runMcpServer(): Promise<void> {\n const encryptionKey = process.env.WALLET_ENCRYPTION_KEY;\n if (!encryptionKey) {\n throw new Error(\"WALLET_ENCRYPTION_KEY environment variable is required\");\n }\n\n const rpcUrl =\n process.env.SOLANA_RPC_URL ?? \"https://api.devnet.solana.com\";\n\n const ctx: WalletContext = { encryptionKey, rpcUrl };\n\n const server = createServer(ctx);\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n // Log to stderr so it doesn't interfere with MCP stdio\n console.error(\n `stng-defi-wallets MCP server started (${TOOLS.length} tools, ${PROMPTS.length} prompts)`,\n );\n}\n\n// Auto-run when executed directly\nconst isDirectRun =\n process.argv[1]?.endsWith(\"server.ts\") ||\n process.argv[1]?.endsWith(\"server.js\");\n\nif (isDirectRun) {\n if (!process.env.WALLET_ENCRYPTION_KEY) {\n console.error(\n \"Error: WALLET_ENCRYPTION_KEY environment variable is required\",\n );\n console.error(\"\");\n console.error(\"Usage:\");\n console.error(\n \" WALLET_ENCRYPTION_KEY=<hex-key> npx tsx src/mcp/server.ts\",\n );\n console.error(\"\");\n console.error(\"Environment variables:\");\n console.error(\n \" WALLET_ENCRYPTION_KEY (required) 32-byte hex key for wallet encryption\",\n );\n console.error(\n \" SOLANA_RPC_URL (optional) Solana RPC, default: https://api.devnet.solana.com\",\n );\n console.error(\"\");\n console.error(\"Generate a key:\");\n console.error(\n \" node -e \\\"console.log(require('crypto').randomBytes(32).toString('hex'))\\\"\",\n );\n process.exit(1);\n }\n\n runMcpServer().catch((error) => {\n console.error(\"Fatal error:\", error);\n process.exit(1);\n });\n}\n","import crypto from \"node:crypto\";\n\nimport { prisma } from \"@/lib/db\";\n\n/** All auditable wallet/agent operations. */\nexport type AuditAction =\n | \"wallet_create\"\n | \"wallet_import\"\n | \"wallet_delete\"\n | \"wallet_list\"\n | \"wallet_get\"\n | \"airdrop_request\"\n | \"airdrop_auto\"\n | \"agent_spawn\"\n | \"agent_list\"\n | \"agent_chat\"\n | \"agent_tool_call\"\n | \"rate_limited\"\n | \"spending_limit_exceeded\"\n | \"spending_limit_update\"\n | \"portfolio_view\"\n | \"strategy_update\"\n | \"analytics_view\"\n | \"agent_monitor\"\n | \"wallet_backup\"\n | \"wallet_restore\"\n | \"signature_invalid\"\n | \"agent_lifecycle\"\n | \"agent_transfer\"\n | \"wallet_rekey\"\n | \"frost_list\"\n | \"frost_create\"\n | \"frost_get\"\n | \"frost_delete\"\n | \"frost_sign\"\n | \"frost_refresh\"\n | \"messaging_command\"\n | \"messaging_bind\"\n | \"messaging_unbind\"\n | \"scheduler_tick\"\n | \"emergency_stop\"\n | \"emergency_resume\"\n | \"kora_status\"\n | \"kora_estimate\"\n | \"kora_sign\";\n\nexport type AuditOutcome = \"success\" | \"failure\" | \"error\";\n\nexport interface AuditLogEntry {\n /** ISO-8601 timestamp */\n timestamp: string;\n /** Sequence number (1-indexed, monotonically increasing) */\n seq: number;\n /** The operation performed */\n action: AuditAction;\n /** Which wallet was affected (if any) */\n walletId?: string;\n /** Which agent triggered this (if applicable) */\n agentId?: string;\n /** Client IP address */\n ip?: string;\n /** Operation outcome */\n outcome: AuditOutcome;\n /** Additional context — varies per action */\n details?: Record<string, unknown>;\n /** HMAC chain hash: HMAC(prevHash + entryData) for tamper detection */\n integrity: string;\n}\n\n/** Lightweight entry before integrity hash is computed. */\ntype RawEntry = Omit<AuditLogEntry, \"integrity\" | \"seq\">;\n\n/**\n * Derive the HMAC key from the wallet encryption key.\n * We use a separate derivation so the audit key is distinct from the wallet key.\n */\nfunction hmacKey(): string {\n return process.env.WALLET_ENCRYPTION_KEY ?? \"audit-fallback-key\";\n}\n\n/** Compute HMAC-SHA256 for chain integrity. */\nfunction computeHmac(prevHash: string, data: string): string {\n return crypto\n .createHmac(\"sha256\", hmacKey())\n .update(prevHash + data)\n .digest(\"hex\");\n}\n\n/**\n * Append an audit entry to the immutable log.\n * Each entry is chained via HMAC to the previous entry for tamper detection.\n */\nexport async function auditLog(raw: RawEntry): Promise<AuditLogEntry> {\n // Read last entry to get previous hash for the HMAC chain\n const lastRow = await prisma.auditLog.findFirst({\n orderBy: { seq: \"desc\" },\n select: { integrity: true, seq: true },\n });\n\n const prevHash = lastRow?.integrity ?? \"genesis\";\n\n // Build the data string (everything except integrity) — seq is auto-incremented\n // We need a temporary seq for the HMAC computation. Use lastRow.seq + 1 as preview.\n const nextSeq = (lastRow?.seq ?? 0) + 1;\n const entryData = JSON.stringify({ ...raw, seq: nextSeq });\n const integrity = computeHmac(prevHash, entryData);\n\n const row = await prisma.auditLog.create({\n data: {\n timestamp: new Date(raw.timestamp),\n action: raw.action,\n walletId: raw.walletId ?? null,\n agentId: raw.agentId ?? null,\n ip: raw.ip ?? null,\n outcome: raw.outcome,\n details: (raw.details as unknown as import(\"@prisma/client/runtime/client\").InputJsonValue) ?? undefined,\n integrity,\n },\n });\n\n const entry: AuditLogEntry = {\n timestamp: raw.timestamp,\n seq: row.seq,\n action: raw.action as AuditAction,\n walletId: raw.walletId,\n agentId: raw.agentId,\n ip: raw.ip,\n outcome: raw.outcome as AuditOutcome,\n details: raw.details,\n integrity: row.integrity,\n };\n\n return entry;\n}\n\nexport interface AuditQueryOptions {\n /** Filter by action type */\n action?: AuditAction;\n /** Filter by wallet ID */\n walletId?: string;\n /** Filter by agent ID */\n agentId?: string;\n /** Only entries after this ISO timestamp */\n after?: string;\n /** Only entries before this ISO timestamp */\n before?: string;\n /** Max entries to return (default 100) */\n limit?: number;\n /** Offset for pagination (default 0) */\n offset?: number;\n}\n\n/** Read and filter audit log entries. Returns newest-first. */\nexport async function readAuditLog(\n opts: AuditQueryOptions = {},\n): Promise<AuditLogEntry[]> {\n const where: Record<string, unknown> = {};\n\n if (opts.action) where.action = opts.action;\n if (opts.walletId) where.walletId = opts.walletId;\n if (opts.agentId) where.agentId = opts.agentId;\n\n if (opts.after || opts.before) {\n const timestampFilter: Record<string, Date> = {};\n if (opts.after) timestampFilter.gt = new Date(opts.after);\n if (opts.before) timestampFilter.lt = new Date(opts.before);\n where.timestamp = timestampFilter;\n }\n\n const rows = await prisma.auditLog.findMany({\n where,\n orderBy: { seq: \"desc\" },\n skip: opts.offset ?? 0,\n take: opts.limit ?? 100,\n });\n\n return rows.map((row) => ({\n timestamp: row.timestamp.toISOString(),\n seq: row.seq,\n action: row.action as AuditAction,\n walletId: row.walletId ?? undefined,\n agentId: row.agentId ?? undefined,\n ip: row.ip ?? undefined,\n outcome: row.outcome as AuditOutcome,\n details: (row.details as Record<string, unknown>) ?? undefined,\n integrity: row.integrity,\n }));\n}\n\n/**\n * Verify the integrity of the entire audit log.\n * Returns { valid: true } if chain is intact, or { valid: false, brokenAt }\n * with the first entry where the chain breaks.\n */\nexport async function verifyAuditIntegrity(): Promise<{\n valid: boolean;\n total: number;\n brokenAt?: number;\n}> {\n const rows = await prisma.auditLog.findMany({\n orderBy: { seq: \"asc\" },\n });\n\n if (rows.length === 0) return { valid: true, total: 0 };\n\n let prevHash = \"genesis\";\n\n for (const row of rows) {\n const entry: Omit<AuditLogEntry, \"integrity\"> = {\n timestamp: row.timestamp.toISOString(),\n seq: row.seq,\n action: row.action as AuditAction,\n walletId: row.walletId ?? undefined,\n agentId: row.agentId ?? undefined,\n ip: row.ip ?? undefined,\n outcome: row.outcome as AuditOutcome,\n details: (row.details as Record<string, unknown>) ?? undefined,\n };\n\n // Reconstruct the raw entry (without integrity and seq) to match what was originally hashed\n const rawForHmac: Record<string, unknown> = {\n timestamp: entry.timestamp,\n action: entry.action,\n outcome: entry.outcome,\n seq: entry.seq,\n };\n if (entry.walletId !== undefined) rawForHmac.walletId = entry.walletId;\n if (entry.agentId !== undefined) rawForHmac.agentId = entry.agentId;\n if (entry.ip !== undefined) rawForHmac.ip = entry.ip;\n if (entry.details !== undefined) rawForHmac.details = entry.details;\n\n const entryData = JSON.stringify(rawForHmac);\n const expected = computeHmac(prevHash, entryData);\n\n if (expected !== row.integrity) {\n return { valid: false, total: rows.length, brokenAt: row.seq };\n }\n prevHash = row.integrity;\n }\n\n return { valid: true, total: rows.length };\n}\n\n/** Extract client IP from a Next.js/Fetch Request object. */\nexport function extractIp(req: Request): string {\n const headers = req.headers;\n // Common proxy headers\n const forwarded = headers.get(\"x-forwarded-for\");\n if (forwarded) return forwarded.split(\",\")[0].trim();\n const realIp = headers.get(\"x-real-ip\");\n if (realIp) return realIp;\n return \"127.0.0.1\";\n}\n","/**\n * Per-wallet spending limits with rolling 24-hour tracking.\n *\n * Provides configurable per-transaction and per-day SOL caps per agent wallet.\n * Spending events are recorded to the database for auditability.\n */\n\nimport { prisma } from \"@/lib/db\";\n\n/** Spending limit configuration for a single wallet. */\nexport interface SpendingLimitsConfig {\n /** Maximum SOL allowed per single transaction (0 = unlimited). */\n maxPerTx: number;\n /** Maximum SOL allowed in a rolling 24-hour window (0 = unlimited). */\n maxPerDay: number;\n /** Whether spending limits are enforced for this wallet. */\n enabled: boolean;\n}\n\n/** A recorded spending event from a tool call. */\nexport interface SpendingRecord {\n timestamp: string;\n walletId: string;\n /** SOL amount spent (positive = outflow). */\n amount: number;\n /** The tool that triggered the spend. */\n toolName: string;\n}\n\n/** Result of a spending limit check. */\nexport interface SpendingCheckResult {\n allowed: boolean;\n reason?: string;\n /** SOL remaining in today's budget (undefined if unlimited). */\n dailyRemaining?: number;\n /** SOL spent in the rolling 24h window. */\n dailySpent: number;\n}\n\n/** Default limits for newly created wallets. */\nexport const DEFAULT_LIMITS: SpendingLimitsConfig = {\n maxPerTx: 1.0,\n maxPerDay: 5.0,\n enabled: true,\n};\n\n/** Rolling window duration (24 hours in milliseconds). */\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\n/** Get the spending limits for a wallet. Returns defaults if none configured. */\nexport async function getSpendingLimits(\n walletId: string,\n): Promise<SpendingLimitsConfig> {\n const row = await prisma.spendingLimits.findUnique({\n where: { walletId },\n });\n\n if (!row) {\n return { ...DEFAULT_LIMITS };\n }\n\n return {\n maxPerTx: row.maxPerTx,\n maxPerDay: row.maxPerDay,\n enabled: row.enabled,\n };\n}\n\n/** Update spending limits for a wallet. Values are validated and clamped. */\nexport async function setSpendingLimits(\n walletId: string,\n config: Partial<SpendingLimitsConfig>,\n): Promise<SpendingLimitsConfig> {\n const current = await getSpendingLimits(walletId);\n\n const updated: SpendingLimitsConfig = {\n maxPerTx:\n typeof config.maxPerTx === \"number\"\n ? Math.max(0, config.maxPerTx)\n : current.maxPerTx,\n maxPerDay:\n typeof config.maxPerDay === \"number\"\n ? Math.max(0, config.maxPerDay)\n : current.maxPerDay,\n enabled:\n typeof config.enabled === \"boolean\" ? config.enabled : current.enabled,\n };\n\n await prisma.spendingLimits.upsert({\n where: { walletId },\n create: {\n walletId,\n maxPerTx: updated.maxPerTx,\n maxPerDay: updated.maxPerDay,\n enabled: updated.enabled,\n },\n update: {\n maxPerTx: updated.maxPerTx,\n maxPerDay: updated.maxPerDay,\n enabled: updated.enabled,\n },\n });\n\n return updated;\n}\n\n/** Delete spending limits and ledger for a wallet (cleanup on wallet delete). */\nexport async function deleteSpendingData(walletId: string): Promise<void> {\n await prisma.$transaction([\n prisma.spendingRecord.deleteMany({ where: { walletId } }),\n prisma.spendingLimits.deleteMany({ where: { walletId } }),\n ]);\n}\n\n/** Record a spending event to the wallet's spending ledger. */\nexport async function recordSpend(\n walletId: string,\n amount: number,\n toolName: string,\n): Promise<SpendingRecord> {\n const row = await prisma.spendingRecord.create({\n data: {\n walletId,\n timestamp: new Date(),\n amount: Math.abs(amount),\n toolName,\n },\n });\n\n return {\n timestamp: row.timestamp.toISOString(),\n walletId: row.walletId,\n amount: row.amount,\n toolName: row.toolName,\n };\n}\n\n/** Get total SOL spent in the rolling 24-hour window. */\nexport async function getDailySpending(walletId: string): Promise<number> {\n const cutoff = new Date(Date.now() - DAY_MS);\n\n const result = await prisma.spendingRecord.aggregate({\n where: {\n walletId,\n timestamp: { gte: cutoff },\n },\n _sum: { amount: true },\n });\n\n const total = result._sum.amount ?? 0;\n return Math.round(total * 1e9) / 1e9; // Avoid floating point drift\n}\n\n/**\n * Check whether a proposed spend is allowed under the wallet's limits.\n *\n * @param walletId - The wallet to check\n * @param proposedAmount - SOL amount of the proposed transaction\n * @returns Check result with allowed/denied and reason\n */\nexport async function checkSpendingLimit(\n walletId: string,\n proposedAmount: number,\n): Promise<SpendingCheckResult> {\n const limits = await getSpendingLimits(walletId);\n const dailySpent = await getDailySpending(walletId);\n\n if (!limits.enabled) {\n return { allowed: true, dailySpent };\n }\n\n // Check per-transaction limit\n if (limits.maxPerTx > 0 && proposedAmount > limits.maxPerTx) {\n return {\n allowed: false,\n reason: `Transaction of ${proposedAmount} SOL exceeds per-transaction limit of ${limits.maxPerTx} SOL`,\n dailyRemaining:\n limits.maxPerDay > 0\n ? Math.max(0, limits.maxPerDay - dailySpent)\n : undefined,\n dailySpent,\n };\n }\n\n // Check daily limit\n if (limits.maxPerDay > 0 && dailySpent + proposedAmount > limits.maxPerDay) {\n const remaining = Math.max(0, limits.maxPerDay - dailySpent);\n return {\n allowed: false,\n reason: `Spending ${proposedAmount} SOL would exceed daily limit of ${limits.maxPerDay} SOL (already spent ${dailySpent.toFixed(4)} SOL, remaining: ${remaining.toFixed(4)} SOL)`,\n dailyRemaining: remaining,\n dailySpent,\n };\n }\n\n return {\n allowed: true,\n dailyRemaining:\n limits.maxPerDay > 0\n ? Math.max(0, limits.maxPerDay - dailySpent - proposedAmount)\n : undefined,\n dailySpent,\n };\n}\n\n/**\n * Extract SOL amount from tool call arguments.\n * Handles common Solana agent kit tool patterns.\n */\nexport function extractSolAmount(\n toolName: string,\n args: Record<string, unknown>,\n): number | undefined {\n // Skip read-only tools (no spending involved) before checking amounts\n const readOnlyPrefixes = [\"get_\", \"fetch_\", \"check_\", \"list_\", \"search_\"];\n if (readOnlyPrefixes.some((p) => toolName.toLowerCase().startsWith(p))) {\n return undefined;\n }\n\n // Direct SOL amount fields (most common pattern)\n for (const key of [\"amount\", \"sol\", \"lamports\", \"quantity\"]) {\n if (typeof args[key] === \"number\" && args[key] > 0) {\n // Convert lamports to SOL if the key is \"lamports\"\n return key === \"lamports\"\n ? (args[key] as number) / 1e9\n : (args[key] as number);\n }\n }\n\n return undefined;\n}\n\n/** Get spending summary for display (limits + current usage). */\nexport async function getSpendingSummary(walletId: string): Promise<{\n limits: SpendingLimitsConfig;\n dailySpent: number;\n dailyRemaining: number | null;\n}> {\n const limits = await getSpendingLimits(walletId);\n const dailySpent = await getDailySpending(walletId);\n const dailyRemaining =\n limits.enabled && limits.maxPerDay > 0\n ? Math.max(0, limits.maxPerDay - dailySpent)\n : null;\n\n return { limits, dailySpent, dailyRemaining };\n}\n","/**\n * Agent lifecycle management — pause, resume, and terminate agents.\n *\n * Each agent wallet has a lifecycle state persisted in the database\n * via Prisma. The state controls whether the agent is allowed to\n * execute chat commands and tool calls.\n *\n * States:\n * - \"active\" — normal operation, agent can execute commands\n * - \"paused\" — temporarily suspended, can be resumed\n * - \"terminated\" — permanently stopped, requires re-creation\n */\n\nimport { prisma } from \"@/lib/db\";\n\n/** Possible lifecycle states for an agent. */\nexport type LifecycleState = \"active\" | \"paused\" | \"terminated\";\n\n/** Valid lifecycle states for input validation. */\nexport const VALID_LIFECYCLE_STATES: LifecycleState[] = [\n \"active\",\n \"paused\",\n \"terminated\",\n];\n\n/** Persisted lifecycle record for a wallet/agent. */\nexport interface AgentLifecycleRecord {\n state: LifecycleState;\n /** ISO timestamp of last state change. */\n updatedAt: string;\n /** Reason for the current state (optional). */\n reason?: string;\n /** History of state transitions (last 10). */\n transitions: LifecycleTransition[];\n}\n\n/** A single state transition event. */\nexport interface LifecycleTransition {\n from: LifecycleState;\n to: LifecycleState;\n timestamp: string;\n reason?: string;\n}\n\n/** Maximum transitions to keep in the history. */\nconst MAX_TRANSITIONS = 10;\n\n/**\n * Valid state transitions:\n * active → paused, active → terminated\n * paused → active, paused → terminated\n * terminated → (none — terminal state)\n */\nconst VALID_TRANSITIONS: Record<LifecycleState, LifecycleState[]> = {\n active: [\"paused\", \"terminated\"],\n paused: [\"active\", \"terminated\"],\n terminated: [],\n};\n\n/** Get the lifecycle state for an agent. Returns \"active\" if none configured. */\nexport async function getAgentLifecycle(walletId: string): Promise<AgentLifecycleRecord> {\n const row = await prisma.agentLifecycle.findUnique({\n where: { walletId },\n });\n\n if (!row) {\n return {\n state: \"active\",\n updatedAt: \"\",\n transitions: [],\n };\n }\n\n const state = VALID_LIFECYCLE_STATES.includes(row.state as LifecycleState)\n ? (row.state as LifecycleState)\n : \"active\";\n\n return {\n state,\n updatedAt: row.updatedAt.toISOString(),\n reason: row.reason ?? undefined,\n transitions: (row.transitions as unknown as LifecycleTransition[]) ?? [],\n };\n}\n\n/**\n * Check if a state transition is valid.\n * Returns an error message if invalid, or null if valid.\n */\nexport function validateTransition(\n from: LifecycleState,\n to: LifecycleState,\n): string | null {\n if (from === to) {\n return `Agent is already in \"${to}\" state`;\n }\n\n const allowed = VALID_TRANSITIONS[from];\n if (!allowed.includes(to)) {\n return `Cannot transition from \"${from}\" to \"${to}\". Terminated agents cannot be restarted.`;\n }\n\n return null;\n}\n\n/**\n * Transition an agent to a new lifecycle state.\n * Validates the transition and persists the new state.\n *\n * @throws Error if the transition is invalid\n */\nexport async function setAgentLifecycle(\n walletId: string,\n newState: LifecycleState,\n reason?: string,\n): Promise<AgentLifecycleRecord> {\n if (!VALID_LIFECYCLE_STATES.includes(newState)) {\n throw new Error(\n `Invalid lifecycle state: ${newState}. Must be one of: ${VALID_LIFECYCLE_STATES.join(\", \")}`,\n );\n }\n\n const current = await getAgentLifecycle(walletId);\n const error = validateTransition(current.state, newState);\n if (error) {\n throw new Error(error);\n }\n\n const now = new Date().toISOString();\n const transition: LifecycleTransition = {\n from: current.state,\n to: newState,\n timestamp: now,\n reason,\n };\n\n // Keep only the last MAX_TRANSITIONS entries\n const transitions = [...current.transitions, transition].slice(\n -MAX_TRANSITIONS,\n );\n\n await prisma.agentLifecycle.upsert({\n where: { walletId },\n update: {\n state: newState,\n reason: reason ?? null,\n transitions: transitions as unknown as import(\"@prisma/client/runtime/client\").InputJsonValue,\n },\n create: {\n walletId,\n state: newState,\n reason: reason ?? null,\n transitions: transitions as unknown as import(\"@prisma/client/runtime/client\").InputJsonValue,\n },\n });\n\n return {\n state: newState,\n updatedAt: now,\n reason,\n transitions,\n };\n}\n\n/** Check whether an agent is allowed to execute commands. */\nexport async function isAgentExecutable(walletId: string): Promise<{\n allowed: boolean;\n state: LifecycleState;\n reason?: string;\n}> {\n const lifecycle = await getAgentLifecycle(walletId);\n\n if (lifecycle.state === \"active\") {\n return { allowed: true, state: \"active\" };\n }\n\n const reason =\n lifecycle.state === \"paused\"\n ? \"Agent is paused. Resume it to continue execution.\"\n : \"Agent is terminated. Create a new agent to continue.\";\n\n return { allowed: false, state: lifecycle.state, reason };\n}\n\n/** Delete lifecycle data for a wallet (cleanup on wallet delete). */\nexport async function deleteLifecycleData(walletId: string): Promise<void> {\n await prisma.agentLifecycle.deleteMany({\n where: { walletId },\n });\n}\n\n/**\n * Human-readable action name for a lifecycle state transition.\n * Used in UI and audit logs.\n */\nexport function lifecycleActionLabel(state: LifecycleState): string {\n switch (state) {\n case \"active\":\n return \"Resume\";\n case \"paused\":\n return \"Pause\";\n case \"terminated\":\n return \"Terminate\";\n }\n}\n","import { prisma } from \"@/lib/db\";\n\nexport interface TxLogEntry {\n timestamp: string;\n walletId: string;\n toolName: string;\n args: Record<string, unknown>;\n result: string;\n success: boolean;\n}\n\nexport async function appendTxLog(\n walletId: string,\n entry: Omit<TxLogEntry, \"timestamp\" | \"walletId\">,\n): Promise<void> {\n await prisma.txLog.create({\n data: {\n walletId,\n timestamp: new Date(),\n toolName: entry.toolName,\n args: entry.args as unknown as import(\"@prisma/client/runtime/client\").InputJsonValue,\n result: entry.result,\n success: entry.success,\n },\n });\n}\n\nexport async function readTxLog(\n walletId: string,\n limit = 50,\n): Promise<TxLogEntry[]> {\n const rows = await prisma.txLog.findMany({\n where: { walletId },\n orderBy: { timestamp: \"desc\" },\n take: limit,\n });\n\n return rows.map((row) => ({\n timestamp: row.timestamp.toISOString(),\n walletId: row.walletId,\n toolName: row.toolName,\n args: row.args as Record<string, unknown>,\n result: row.result,\n success: row.success,\n }));\n}\n","/**\n * Agent strategy presets — configurable risk profiles per wallet.\n *\n * Each wallet/agent can be assigned a strategy (conservative / balanced / aggressive)\n * that affects risk tolerance, position sizing, protocol selection, and autonomous\n * decision-making via system prompt injection.\n */\n\nimport { prisma } from \"@/lib/db\";\n\n/** Available strategy identifiers. */\nexport type StrategyId = \"conservative\" | \"balanced\" | \"aggressive\";\n\n/** Describes a single strategy preset with all behavioral parameters. */\nexport interface StrategyPreset {\n id: StrategyId;\n name: string;\n description: string;\n /** Max fraction of wallet balance to use per trade (0–1). */\n maxPositionSize: number;\n /** Slippage tolerance in basis points (e.g., 100 = 1%). */\n slippageBps: number;\n /** Whether leverage/perpetual protocols are allowed. */\n allowLeverage: boolean;\n /** Whether new/unverified tokens are allowed. */\n allowUnverifiedTokens: boolean;\n /** Maximum number of concurrent open positions. */\n maxOpenPositions: number;\n /** Protocols the agent should prefer or avoid. */\n protocolGuidance: string;\n /** Additional system prompt instructions for the agent. */\n promptGuidance: string;\n}\n\n/** Persisted strategy selection for a wallet. */\nexport interface WalletStrategy {\n strategyId: StrategyId;\n updatedAt: string;\n}\n\n/** All available strategy presets. */\nexport const STRATEGY_PRESETS: Record<StrategyId, StrategyPreset> = {\n conservative: {\n id: \"conservative\",\n name: \"Conservative\",\n description:\n \"Low risk — small positions, only well-known protocols, no leverage. Prioritizes capital preservation.\",\n maxPositionSize: 0.1,\n slippageBps: 100,\n allowLeverage: false,\n allowUnverifiedTokens: false,\n maxOpenPositions: 2,\n protocolGuidance:\n \"Only use well-established protocols: Jupiter (swaps), Marinade/Jito (liquid staking). Avoid Drift, Adrena, and leveraged products.\",\n promptGuidance: [\n \"You are operating in CONSERVATIVE mode. Capital preservation is your top priority.\",\n \"- Maximum position size: 10% of wallet balance per trade\",\n \"- Use tight slippage (1% / 100 bps) — reject trades with higher slippage\",\n \"- Only interact with well-known, audited protocols (Jupiter, Orca, Marinade, Jito)\",\n \"- Do NOT use leverage, perpetuals, or margin trading\",\n \"- Do NOT trade unverified or low-liquidity tokens\",\n \"- Maximum 2 open positions at a time\",\n \"- Always check balance before and after trades\",\n \"- Prefer liquid staking (mSOL, jitoSOL) over risky DeFi yields\",\n \"- If unsure about a trade, err on the side of NOT executing it\",\n ].join(\"\\n\"),\n },\n\n balanced: {\n id: \"balanced\",\n name: \"Balanced\",\n description:\n \"Moderate risk — standard positions, most protocols allowed, moderate slippage tolerance. Good all-around strategy.\",\n maxPositionSize: 0.25,\n slippageBps: 300,\n allowLeverage: false,\n allowUnverifiedTokens: false,\n maxOpenPositions: 5,\n protocolGuidance:\n \"Use established protocols freely: Jupiter, Orca, Raydium, Meteora for swaps/LP, Marinade/Jito for staking, Lulo for lending. Avoid leverage protocols.\",\n promptGuidance: [\n \"You are operating in BALANCED mode. Seek reasonable returns while managing risk.\",\n \"- Maximum position size: 25% of wallet balance per trade\",\n \"- Standard slippage tolerance (3% / 300 bps)\",\n \"- Use established protocols: Jupiter, Orca, Raydium, Meteora, Marinade, Jito, Lulo\",\n \"- Do NOT use leverage or perpetual trading\",\n \"- Only trade tokens with reasonable liquidity\",\n \"- Maximum 5 open positions at a time\",\n \"- Diversify across different protocols and strategies when possible\",\n \"- Provide liquidity on concentrated AMMs only if the range is reasonable\",\n \"- Monitor position health and rebalance if needed\",\n ].join(\"\\n\"),\n },\n\n aggressive: {\n id: \"aggressive\",\n name: \"Aggressive\",\n description:\n \"High risk — large positions, all protocols including leverage, wider slippage. Maximizes upside potential.\",\n maxPositionSize: 0.5,\n slippageBps: 500,\n allowLeverage: true,\n allowUnverifiedTokens: true,\n maxOpenPositions: 10,\n protocolGuidance:\n \"All protocols available: Jupiter, Orca, Raydium, Meteora, Drift, Adrena, Lulo, and experimental protocols. Leverage and perps are allowed.\",\n promptGuidance: [\n \"You are operating in AGGRESSIVE mode. Maximize returns — higher risk is acceptable.\",\n \"- Maximum position size: 50% of wallet balance per trade\",\n \"- Wide slippage tolerance (5% / 500 bps) for fast execution\",\n \"- All protocols available including Drift and Adrena for perpetuals\",\n \"- Leverage and margin trading are ALLOWED — use up to 3x leverage max\",\n \"- New and trending tokens are allowed if requested\",\n \"- Maximum 10 open positions at a time\",\n \"- Actively seek yield farming and liquidity provision opportunities\",\n \"- Execute multi-step DeFi strategies: swap → LP → stake for compound yields\",\n \"- Act decisively — speed of execution matters\",\n ].join(\"\\n\"),\n },\n};\n\n/** Valid strategy IDs for input validation. */\nexport const VALID_STRATEGY_IDS: StrategyId[] = [\n \"conservative\",\n \"balanced\",\n \"aggressive\",\n];\n\n/** Default strategy for new wallets. */\nexport const DEFAULT_STRATEGY_ID: StrategyId = \"balanced\";\n\n/** Get the strategy assigned to a wallet. Returns default if none configured. */\nexport async function getWalletStrategy(walletId: string): Promise<WalletStrategy> {\n const row = await prisma.agentStrategyConfig.findUnique({\n where: { walletId },\n });\n\n if (!row) {\n return { strategyId: DEFAULT_STRATEGY_ID, updatedAt: \"\" };\n }\n\n const id = VALID_STRATEGY_IDS.includes(row.strategyId as StrategyId)\n ? (row.strategyId as StrategyId)\n : DEFAULT_STRATEGY_ID;\n\n return {\n strategyId: id,\n updatedAt: row.updatedAt.toISOString(),\n };\n}\n\n/** Set the strategy for a wallet. Validates the strategy ID. */\nexport async function setWalletStrategy(\n walletId: string,\n strategyId: StrategyId,\n): Promise<WalletStrategy> {\n if (!VALID_STRATEGY_IDS.includes(strategyId)) {\n throw new Error(\n `Invalid strategy: ${strategyId}. Must be one of: ${VALID_STRATEGY_IDS.join(\", \")}`,\n );\n }\n\n const row = await prisma.agentStrategyConfig.upsert({\n where: { walletId },\n update: { strategyId },\n create: { walletId, strategyId },\n });\n\n return {\n strategyId: row.strategyId as StrategyId,\n updatedAt: row.updatedAt.toISOString(),\n };\n}\n\n/** Delete strategy data for a wallet (cleanup on wallet delete). */\nexport async function deleteStrategyData(walletId: string): Promise<void> {\n await prisma.agentStrategyConfig.deleteMany({\n where: { walletId },\n });\n}\n\n/** Get the full strategy preset for a wallet. */\nexport async function getWalletStrategyPreset(walletId: string): Promise<StrategyPreset> {\n const { strategyId } = await getWalletStrategy(walletId);\n return STRATEGY_PRESETS[strategyId];\n}\n\n/**\n * Build a system prompt section for the agent's current strategy.\n * Injected into the LLM context so the agent follows the right risk profile.\n */\nexport async function getStrategyPrompt(walletId: string): Promise<string> {\n const preset = await getWalletStrategyPreset(walletId);\n return (\n `\\n\\n## Agent Strategy: ${preset.name} (ACTIVE)\\n` +\n `${preset.promptGuidance}\\n\\n` +\n `### Protocol Guidance\\n${preset.protocolGuidance}\\n\\n` +\n `### Risk Parameters\\n` +\n `- Max position size: ${(preset.maxPositionSize * 100).toFixed(0)}% of wallet balance\\n` +\n `- Slippage tolerance: ${preset.slippageBps} bps (${(preset.slippageBps / 100).toFixed(1)}%)\\n` +\n `- Leverage allowed: ${preset.allowLeverage ? \"Yes (max 3x)\" : \"No\"}\\n` +\n `- Unverified tokens: ${preset.allowUnverifiedTokens ? \"Allowed\" : \"Not allowed\"}\\n` +\n `- Max open positions: ${preset.maxOpenPositions}`\n );\n}\n","/**\n * Agent monitoring — enriched per-agent status for the monitoring dashboard.\n *\n * Aggregates wallet info, balance, last action, status, strategy,\n * and spending data across all agent wallets.\n */\n\nimport type { StoredWallet } from \"./wallet-store\";\nimport type { WalletManager } from \"./wallet-manager\";\nimport type { TxLogEntry } from \"./tx-log\";\nimport type { StrategyId } from \"./agent-strategy\";\nimport { readTxLog } from \"./tx-log\";\nimport { getWalletStrategy } from \"./agent-strategy\";\nimport { getSpendingSummary } from \"./spending-limits\";\nimport { getAgentLifecycle } from \"./agent-lifecycle\";\nimport type { LifecycleState } from \"./agent-lifecycle\";\n\n/** Operational status of an agent. */\nexport type AgentStatus = \"idle\" | \"executing\" | \"error\" | \"paused\" | \"terminated\";\n\n/** Spending snapshot for an agent. */\nexport interface AgentSpending {\n dailySpent: number;\n dailyLimit: number;\n dailyRemaining: number | null;\n enabled: boolean;\n}\n\n/** A single agent's monitoring entry. */\nexport interface AgentMonitorEntry {\n id: string;\n publicKey: string;\n label: string;\n createdAt: string;\n balance: number;\n status: AgentStatus;\n lifecycleState: LifecycleState;\n strategy: StrategyId;\n lastAction: AgentLastAction | null;\n spending: AgentSpending;\n totalActions: number;\n successRate: number;\n}\n\n/** Last action taken by an agent. */\nexport interface AgentLastAction {\n toolName: string;\n success: boolean;\n timestamp: string;\n}\n\n/** Full monitoring response. */\nexport interface AgentMonitorData {\n agents: AgentMonitorEntry[];\n totalAgents: number;\n activeAgents: number;\n errorAgents: number;\n pausedAgents: number;\n terminatedAgents: number;\n fetchedAt: string;\n}\n\n/** Threshold in milliseconds — if last action was within this window, agent is \"executing\". */\nconst EXECUTING_THRESHOLD_MS = 30_000;\n\n/**\n * Determine agent status from recent transaction log entries.\n *\n * - \"executing\" if the most recent action was within the last 30 seconds\n * - \"error\" if the most recent action failed\n * - \"idle\" otherwise (or if no actions at all)\n */\nexport function deriveStatus(lastAction: AgentLastAction | null): AgentStatus {\n if (!lastAction) return \"idle\";\n\n const elapsed = Date.now() - new Date(lastAction.timestamp).getTime();\n\n if (elapsed < EXECUTING_THRESHOLD_MS) {\n return lastAction.success ? \"executing\" : \"error\";\n }\n\n if (!lastAction.success) return \"error\";\n\n return \"idle\";\n}\n\n/**\n * Extract the most recent action from a wallet's tx log.\n */\nexport function extractLastAction(entries: TxLogEntry[]): AgentLastAction | null {\n if (entries.length === 0) return null;\n // readTxLog returns newest-first (already reversed)\n const latest = entries[0];\n return {\n toolName: latest.toolName,\n success: latest.success,\n timestamp: latest.timestamp,\n };\n}\n\n/**\n * Calculate success rate from tx log entries.\n * Returns 1.0 when there are no entries (no failures).\n */\nexport function calcSuccessRate(entries: TxLogEntry[]): number {\n if (entries.length === 0) return 1.0;\n const successes = entries.filter((e) => e.success).length;\n return successes / entries.length;\n}\n\n/**\n * Build a single agent's monitoring entry from its wallet and tx log.\n */\nexport async function buildAgentEntry(\n wallet: StoredWallet,\n balance: number,\n txEntries: TxLogEntry[],\n): Promise<AgentMonitorEntry> {\n const lastAction = extractLastAction(txEntries);\n const lifecycle = await getAgentLifecycle(wallet.id);\n\n // Lifecycle state overrides operational status\n const status: AgentStatus =\n lifecycle.state === \"paused\"\n ? \"paused\"\n : lifecycle.state === \"terminated\"\n ? \"terminated\"\n : deriveStatus(lastAction);\n\n const strategy = await getWalletStrategy(wallet.id);\n const spending = await getSpendingSummary(wallet.id);\n\n return {\n id: wallet.id,\n publicKey: wallet.publicKey,\n label: wallet.label || `Agent ${wallet.id}`,\n createdAt: wallet.createdAt,\n balance,\n status,\n lifecycleState: lifecycle.state,\n strategy: strategy.strategyId,\n lastAction,\n spending: {\n dailySpent: spending.dailySpent,\n dailyLimit: spending.limits.maxPerDay,\n dailyRemaining: spending.dailyRemaining,\n enabled: spending.limits.enabled,\n },\n totalActions: txEntries.length,\n successRate: calcSuccessRate(txEntries),\n };\n}\n\n/**\n * Build the full agent monitoring dashboard data.\n *\n * Fetches balances in parallel for all wallets, then enriches\n * each with tx log, strategy, and spending data.\n */\nexport async function buildAgentMonitor(\n manager: WalletManager,\n): Promise<AgentMonitorData> {\n // Only include agent wallets — standalone wallets (isAgent: false) are excluded\n const allWallets = await manager.listWallets();\n const wallets = allWallets.filter((w) => w.isAgent !== false);\n\n const entries = await Promise.all(\n wallets.map(async (wallet) => {\n let balance = 0;\n try {\n balance = await manager.getBalance(wallet.publicKey);\n } catch {\n /* RPC may be unavailable */\n }\n\n const txEntries = await readTxLog(wallet.id, 50);\n return buildAgentEntry(wallet, balance, txEntries);\n }),\n );\n\n // Sort: executing first, then error, then idle, then paused, then terminated.\n const statusOrder: Record<AgentStatus, number> = {\n executing: 0,\n error: 1,\n idle: 2,\n paused: 3,\n terminated: 4,\n };\n\n entries.sort((a, b) => {\n const statusDiff = statusOrder[a.status] - statusOrder[b.status];\n if (statusDiff !== 0) return statusDiff;\n return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();\n });\n\n return {\n agents: entries,\n totalAgents: entries.length,\n activeAgents: entries.filter((e) => e.status === \"executing\").length,\n errorAgents: entries.filter((e) => e.status === \"error\").length,\n pausedAgents: entries.filter((e) => e.status === \"paused\").length,\n terminatedAgents: entries.filter((e) => e.status === \"terminated\").length,\n fetchedAt: new Date().toISOString(),\n };\n}\n","/**\n * Portfolio aggregation — fetches SOL + SPL token balances across all wallets\n * and resolves USD prices via Jupiter Price API v2.\n */\n\nimport { WalletManager, type TokenBalance } from \"./wallet-manager\";\n\n/** SOL native mint used by Jupiter Price API */\nexport const SOL_MINT = \"So11111111111111111111111111111111111111112\";\n\n/** Jupiter Price API v2 base URL */\nconst JUPITER_PRICE_URL = \"https://api.jup.ag/price/v2\";\n\n/** Price fetch timeout in milliseconds */\nconst PRICE_TIMEOUT_MS = 8_000;\n\n/** Well-known Solana token symbols (mainnet mints) */\nconst KNOWN_TOKENS: Record<string, string> = {\n [SOL_MINT]: \"SOL\",\n EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v: \"USDC\",\n Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB: \"USDT\",\n mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So: \"mSOL\",\n \"7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj\": \"stSOL\",\n DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263: \"BONK\",\n JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN: \"JUP\",\n};\n\n/** Resolve a human-readable symbol for a token mint */\nexport function resolveSymbol(mint: string): string {\n return KNOWN_TOKENS[mint] ?? `${mint.slice(0, 4)}…${mint.slice(-4)}`;\n}\n\nexport interface TokenHolding {\n mint: string;\n symbol: string;\n balance: number;\n decimals: number;\n priceUsd: number | null;\n valueUsd: number | null;\n}\n\nexport interface WalletHoldings {\n walletId: string;\n publicKey: string;\n label: string;\n solBalance: number;\n solValueUsd: number | null;\n tokens: TokenHolding[];\n totalValueUsd: number | null;\n}\n\nexport interface AllocationEntry {\n label: string;\n mint: string;\n valueUsd: number;\n percentage: number;\n color: string;\n}\n\nexport interface PortfolioSummary {\n totalValueUsd: number | null;\n solPrice: number | null;\n walletCount: number;\n wallets: WalletHoldings[];\n allocation: AllocationEntry[];\n fetchedAt: string;\n}\n\n/** Predefined colors for allocation chart segments */\nconst ALLOC_COLORS = [\n \"#22c55e\", \"#3b82f6\", \"#f59e0b\", \"#ef4444\", \"#8b5cf6\",\n \"#ec4899\", \"#06b6d4\", \"#f97316\", \"#14b8a6\", \"#6366f1\",\n];\n\n/** Fetch token prices from Jupiter Price API v2 (best-effort). */\nexport async function fetchPrices(mints: string[]): Promise<Map<string, number>> {\n const prices = new Map<string, number>();\n if (mints.length === 0) return prices;\n\n try {\n const ids = mints.join(\",\");\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), PRICE_TIMEOUT_MS);\n\n const res = await fetch(`${JUPITER_PRICE_URL}?ids=${ids}`, {\n signal: controller.signal,\n });\n clearTimeout(timer);\n\n if (!res.ok) return prices;\n\n const json = (await res.json()) as {\n data?: Record<string, { price?: string }>;\n };\n\n for (const [mint, info] of Object.entries(json.data ?? {})) {\n if (info.price) {\n const p = parseFloat(info.price);\n if (Number.isFinite(p) && p > 0) prices.set(mint, p);\n }\n }\n } catch {\n // Price fetch is best-effort — return empty map\n }\n\n return prices;\n}\n\n/** Build a complete portfolio summary across all wallets. */\nexport async function buildPortfolio(\n manager: WalletManager,\n): Promise<PortfolioSummary> {\n const wallets = await manager.listWallets();\n\n // Fetch balances for all wallets in parallel\n const walletData = await Promise.all(\n wallets.map(async (w) => {\n const [solBalance, tokens] = await Promise.all([\n manager.getBalance(w.publicKey).catch(() => 0),\n manager.getTokenBalances(w.publicKey).catch((): TokenBalance[] => []),\n ]);\n return { wallet: w, solBalance, tokens };\n }),\n );\n\n // Collect unique mints for price lookup\n const allMints = new Set<string>([SOL_MINT]);\n for (const { tokens } of walletData) {\n for (const t of tokens) allMints.add(t.mint);\n }\n\n const prices = await fetchPrices([...allMints]);\n const solPrice = prices.get(SOL_MINT) ?? null;\n\n // Build per-wallet holdings\n const walletHoldings: WalletHoldings[] = walletData.map(\n ({ wallet, solBalance, tokens }) => {\n const solValueUsd = solPrice !== null ? solBalance * solPrice : null;\n\n const tokenHoldings: TokenHolding[] = tokens.map((t) => {\n const priceUsd = prices.get(t.mint) ?? null;\n const valueUsd = priceUsd !== null ? t.balance * priceUsd : null;\n return {\n mint: t.mint,\n symbol: resolveSymbol(t.mint),\n balance: t.balance,\n decimals: t.decimals,\n priceUsd,\n valueUsd,\n };\n });\n\n const tokenValueSum = tokenHoldings.reduce(\n (s, t) => s + (t.valueUsd ?? 0),\n 0,\n );\n const totalValueUsd =\n solValueUsd !== null ? solValueUsd + tokenValueSum : null;\n\n return {\n walletId: wallet.id,\n publicKey: wallet.publicKey,\n label: wallet.label || `Agent ${wallet.id.slice(0, 6)}`,\n solBalance,\n solValueUsd,\n tokens: tokenHoldings,\n totalValueUsd,\n };\n },\n );\n\n // Aggregate allocation across all wallets\n const allocMap = new Map<string, { label: string; valueUsd: number }>();\n const totalSolValue = walletHoldings.reduce(\n (s, w) => s + (w.solValueUsd ?? 0),\n 0,\n );\n if (totalSolValue > 0) {\n allocMap.set(SOL_MINT, { label: \"SOL\", valueUsd: totalSolValue });\n }\n for (const wh of walletHoldings) {\n for (const t of wh.tokens) {\n if (t.valueUsd && t.valueUsd > 0) {\n const ex = allocMap.get(t.mint);\n if (ex) ex.valueUsd += t.valueUsd;\n else allocMap.set(t.mint, { label: t.symbol, valueUsd: t.valueUsd });\n }\n }\n }\n\n const totalValueUsd = walletHoldings.reduce(\n (s, w) => s + (w.totalValueUsd ?? 0),\n 0,\n );\n\n const sortedAlloc = [...allocMap.entries()].sort(\n (a, b) => b[1].valueUsd - a[1].valueUsd,\n );\n const allocation: AllocationEntry[] = sortedAlloc.map(\n ([mint, { label, valueUsd }], i) => ({\n label,\n mint,\n valueUsd,\n percentage: totalValueUsd > 0 ? (valueUsd / totalValueUsd) * 100 : 0,\n color: ALLOC_COLORS[i % ALLOC_COLORS.length],\n }),\n );\n\n return {\n totalValueUsd: solPrice !== null ? totalValueUsd : null,\n solPrice,\n walletCount: wallets.length,\n wallets: walletHoldings,\n allocation,\n fetchedAt: new Date().toISOString(),\n };\n}\n","/**\n * Fraud Monitor — stateful detector that tracks transaction patterns\n * per wallet and raises alerts on suspicious activity.\n *\n * Detection rules:\n * - Rapid drain: >3 outbound transfers in 60s\n * - Velocity spike: current hour spend > 5x rolling 24h average\n * - New program interaction: first-ever use of a program ID\n * - Authority change attempt: any setAuthority/assign on wallet accounts\n * - Repeated failures: >5 failed txs in 10 min\n *\n * In-memory state is used for detection logic. Only alerts are persisted\n * to the database via Prisma.\n */\n\nimport crypto from \"node:crypto\";\nimport { prisma } from \"@/lib/db\";\nimport { analyzeInstructions, type TxLabel } from \"./tx-analyzer\";\nimport { notify } from \"./notifications\";\nimport { getDailySpending } from \"./spending-limits\";\nimport { auditLog } from \"./audit-log\";\nimport type { TransactionInstruction } from \"@solana/web3.js\";\n\nexport type FraudRule =\n | \"rapid_drain\"\n | \"velocity_spike\"\n | \"new_program\"\n | \"authority_change\"\n | \"repeated_failures\";\n\nexport interface FraudAlert {\n id: string;\n walletId: string;\n rule: FraudRule;\n severity: \"low\" | \"medium\" | \"high\" | \"critical\";\n timestamp: string;\n details: Record<string, unknown>;\n acknowledged: boolean;\n}\n\nexport interface TxRecord {\n /** Outbound SOL amount (0 for non-transfers) */\n amount: number;\n /** Whether the tx succeeded */\n success: boolean;\n /** Program IDs involved */\n programs: string[];\n /** Labels from tx-analyzer */\n labels: TxLabel[];\n /** Timestamp override (for testing) */\n timestamp?: string;\n /** Optional instructions for deeper analysis */\n instructions?: TransactionInstruction[];\n}\n\nexport interface FraudStats {\n totalAlerts: number;\n unacknowledged: number;\n bySeverity: Record<string, number>;\n byRule: Record<string, number>;\n}\n\ninterface InternalState {\n /** Recent outbound transfer timestamps (for rapid drain) */\n recentTransfers: number[];\n /** Recent failure timestamps (for repeated failures) */\n recentFailures: number[];\n /** Hourly spend amounts (for velocity spike) */\n hourlySpend: { hour: number; amount: number }[];\n /** Known program IDs this wallet has interacted with */\n knownPrograms: Set<string>;\n}\n\n// In-memory state per wallet (used for detection rules only)\nconst stateCache = new Map<string, InternalState>();\n\nfunction getState(walletId: string): InternalState {\n if (stateCache.has(walletId)) return stateCache.get(walletId)!;\n\n const state: InternalState = {\n recentTransfers: [],\n recentFailures: [],\n hourlySpend: [],\n knownPrograms: new Set(),\n };\n stateCache.set(walletId, state);\n return state;\n}\n\n/**\n * Record a transaction and check all fraud rules.\n * Returns any new alerts generated.\n */\nexport async function recordTransaction(\n walletId: string,\n txInfo: TxRecord,\n): Promise<FraudAlert[]> {\n const state = getState(walletId);\n const now = txInfo.timestamp ? new Date(txInfo.timestamp).getTime() : Date.now();\n const alerts: FraudAlert[] = [];\n\n // Update state\n if (txInfo.amount > 0 && txInfo.success) {\n state.recentTransfers.push(now);\n const currentHour = Math.floor(now / 3600000);\n const hourEntry = state.hourlySpend.find((h) => h.hour === currentHour);\n if (hourEntry) {\n hourEntry.amount += txInfo.amount;\n } else {\n state.hourlySpend.push({ hour: currentHour, amount: txInfo.amount });\n }\n }\n\n if (!txInfo.success) {\n state.recentFailures.push(now);\n }\n\n // --- Rule 1: Rapid drain (>3 outbound in 60s) ---\n const recentWindow = now - 60_000;\n state.recentTransfers = state.recentTransfers.filter((t) => t > recentWindow - 300_000); // keep 5 min\n const transfersInWindow = state.recentTransfers.filter((t) => t > recentWindow).length;\n if (transfersInWindow > 3) {\n alerts.push(createAlert(walletId, \"rapid_drain\", \"critical\", {\n transferCount: transfersInWindow,\n windowSeconds: 60,\n }));\n }\n\n // --- Rule 2: Velocity spike (current hour > 5x 24h average) ---\n if (txInfo.amount > 0 && txInfo.success) {\n const dailySpend = await getDailySpending(walletId);\n const hourlyAvg = dailySpend / 24;\n const currentHour = Math.floor(now / 3600000);\n const currentHourSpend = state.hourlySpend.find((h) => h.hour === currentHour)?.amount ?? 0;\n\n if (hourlyAvg > 0 && currentHourSpend > hourlyAvg * 5) {\n alerts.push(createAlert(walletId, \"velocity_spike\", \"high\", {\n currentHourSpend,\n hourlyAvg: Math.round(hourlyAvg * 1e6) / 1e6,\n multiplier: Math.round((currentHourSpend / hourlyAvg) * 10) / 10,\n }));\n }\n }\n\n // --- Rule 3: New program interaction ---\n for (const prog of txInfo.programs) {\n if (!state.knownPrograms.has(prog)) {\n state.knownPrograms.add(prog);\n // Only alert after the wallet has some history (>2 known programs)\n if (state.knownPrograms.size > 2) {\n alerts.push(createAlert(walletId, \"new_program\", \"medium\", {\n programId: prog,\n totalKnown: state.knownPrograms.size,\n }));\n }\n }\n }\n\n // --- Rule 4: Authority change ---\n if (txInfo.labels.includes(\"authority_change\")) {\n alerts.push(createAlert(walletId, \"authority_change\", \"critical\", {\n labels: txInfo.labels,\n }));\n }\n\n // --- Rule 5: Repeated failures (>5 in 10 min) ---\n const failureWindow = now - 600_000;\n state.recentFailures = state.recentFailures.filter((t) => t > failureWindow - 300_000);\n const failuresInWindow = state.recentFailures.filter((t) => t > failureWindow).length;\n if (failuresInWindow > 5) {\n alerts.push(createAlert(walletId, \"repeated_failures\", \"medium\", {\n failureCount: failuresInWindow,\n windowMinutes: 10,\n }));\n }\n\n // Persist alerts to database and send notifications\n if (alerts.length > 0) {\n await persistAlerts(alerts);\n for (const alert of alerts) {\n await notify(walletId, {\n type: \"fraud_alert\",\n priority: alert.severity,\n title: `Fraud Alert: ${alert.rule}`,\n message: formatAlertMessage(alert),\n metadata: alert.details,\n });\n\n await auditLog({\n timestamp: new Date().toISOString(),\n action: \"agent_tool_call\",\n walletId,\n outcome: \"success\",\n details: { fraudRule: alert.rule, severity: alert.severity, alertId: alert.id },\n });\n }\n }\n\n return alerts;\n}\n\n/**\n * Get fraud alerts for a wallet.\n */\nexport async function getAlerts(\n walletId: string,\n opts?: { unacknowledgedOnly?: boolean },\n): Promise<FraudAlert[]> {\n const where: { walletId: string; acknowledged?: boolean } = { walletId };\n if (opts?.unacknowledgedOnly) {\n where.acknowledged = false;\n }\n\n const rows = await prisma.fraudAlert.findMany({\n where,\n orderBy: { timestamp: \"desc\" },\n });\n\n return rows.map((row) => ({\n id: row.id,\n walletId: row.walletId,\n rule: row.rule as FraudRule,\n severity: row.severity as FraudAlert[\"severity\"],\n timestamp: row.timestamp.toISOString(),\n details: row.details as Record<string, unknown>,\n acknowledged: row.acknowledged,\n }));\n}\n\n/**\n * Acknowledge a fraud alert.\n */\nexport async function acknowledgeAlert(walletId: string, alertId: string): Promise<boolean> {\n try {\n await prisma.fraudAlert.update({\n where: { id: alertId, walletId },\n data: { acknowledged: true },\n });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Get fraud statistics for a wallet.\n */\nexport async function getFraudStats(walletId: string): Promise<FraudStats> {\n const alerts = await getAlerts(walletId);\n\n const bySeverity: Record<string, number> = {};\n const byRule: Record<string, number> = {};\n let unacknowledged = 0;\n\n for (const a of alerts) {\n bySeverity[a.severity] = (bySeverity[a.severity] ?? 0) + 1;\n byRule[a.rule] = (byRule[a.rule] ?? 0) + 1;\n if (!a.acknowledged) unacknowledged++;\n }\n\n return {\n totalAlerts: alerts.length,\n unacknowledged,\n bySeverity,\n byRule,\n };\n}\n\n/**\n * Reset in-memory state and database alerts for a wallet.\n */\nexport async function resetState(walletId: string): Promise<void> {\n stateCache.delete(walletId);\n await prisma.fraudAlert.deleteMany({\n where: { walletId },\n });\n}\n\n/** Reset all in-memory state and database alerts. */\nexport async function resetAllState(): Promise<void> {\n stateCache.clear();\n await prisma.fraudAlert.deleteMany();\n}\n\n// --- Internal helpers ---\n\nfunction createAlert(\n walletId: string,\n rule: FraudRule,\n severity: FraudAlert[\"severity\"],\n details: Record<string, unknown>,\n): FraudAlert {\n return {\n id: crypto.randomUUID().slice(0, 8),\n walletId,\n rule,\n severity,\n timestamp: new Date().toISOString(),\n details,\n acknowledged: false,\n };\n}\n\nasync function persistAlerts(alerts: FraudAlert[]): Promise<void> {\n await prisma.fraudAlert.createMany({\n data: alerts.map((a) => ({\n id: a.id,\n walletId: a.walletId,\n rule: a.rule,\n severity: a.severity,\n timestamp: new Date(a.timestamp),\n details: a.details as unknown as import(\"@prisma/client/runtime/client\").InputJsonValue,\n acknowledged: a.acknowledged,\n })),\n });\n}\n\nfunction formatAlertMessage(alert: FraudAlert): string {\n switch (alert.rule) {\n case \"rapid_drain\":\n return `Rapid drain detected: ${alert.details.transferCount} outbound transfers in ${alert.details.windowSeconds}s`;\n case \"velocity_spike\":\n return `Velocity spike: current hour spend is ${alert.details.multiplier}x the 24h average`;\n case \"new_program\":\n return `New program interaction: ${alert.details.programId}`;\n case \"authority_change\":\n return \"Authority change attempt detected on wallet accounts\";\n case \"repeated_failures\":\n return `${alert.details.failureCount} failed transactions in ${alert.details.windowMinutes} minutes`;\n }\n}\n","/**\n * Notification Service — central dispatch for wallet security and payment alerts.\n *\n * Routes notifications via the SSE EventBus for real-time delivery and persists\n * notification history to PostgreSQL via Prisma.\n */\n\nimport crypto from \"node:crypto\";\nimport { prisma } from \"@/lib/db\";\nimport { eventBus } from \"./event-bus\";\n\nexport type NotificationType =\n | \"fraud_alert\"\n | \"spending_warning\"\n | \"payment_executed\"\n | \"payment_failed\"\n | \"subscription_due\"\n | \"subscription_failed\"\n | \"authority_change\"\n | \"policy_violation\"\n | \"refresh_needed\"\n | \"strategy_executed\"\n | \"strategy_failed\"\n | \"strategy_paused\";\n\nexport interface Notification {\n id: string;\n walletId: string;\n type: NotificationType;\n priority: \"low\" | \"medium\" | \"high\" | \"critical\";\n title: string;\n message: string;\n timestamp: string;\n read: boolean;\n metadata?: Record<string, unknown>;\n}\n\n/** Map a Prisma row to our public interface. */\nfunction toNotification(row: {\n id: string;\n walletId: string;\n type: string;\n priority: string;\n title: string;\n message: string;\n timestamp: Date;\n read: boolean;\n metadata: unknown;\n}): Notification {\n return {\n id: row.id,\n walletId: row.walletId,\n type: row.type as NotificationType,\n priority: row.priority as Notification[\"priority\"],\n title: row.title,\n message: row.message,\n timestamp: row.timestamp.toISOString(),\n read: row.read,\n ...(row.metadata ? { metadata: row.metadata as Record<string, unknown> } : {}),\n };\n}\n\n/**\n * Send a notification for a wallet.\n * Persists to database and emits an SSE event.\n */\nexport async function notify(\n walletId: string,\n partial: Omit<Notification, \"id\" | \"timestamp\" | \"read\" | \"walletId\">,\n): Promise<Notification> {\n const row = await prisma.notification.create({\n data: {\n id: crypto.randomUUID().slice(0, 8),\n walletId,\n type: partial.type,\n priority: partial.priority,\n title: partial.title,\n message: partial.message,\n read: false,\n metadata: (partial.metadata as unknown as import(\"@prisma/client/runtime/client\").InputJsonValue) ?? undefined,\n },\n });\n\n const notification = toNotification(row);\n\n // Emit SSE event for real-time delivery\n eventBus.emit({\n type: \"notification\" as never, // extended event type\n walletId,\n data: notification as unknown as Record<string, unknown>,\n });\n\n return notification;\n}\n\n/**\n * Get notifications for a wallet with optional filtering.\n */\nexport async function getNotifications(\n walletId: string,\n opts?: { unreadOnly?: boolean; limit?: number },\n): Promise<Notification[]> {\n const where: { walletId: string; read?: boolean } = { walletId };\n if (opts?.unreadOnly) {\n where.read = false;\n }\n\n const limit = opts?.limit ?? 100;\n\n const rows = await prisma.notification.findMany({\n where,\n orderBy: { timestamp: \"desc\" },\n take: limit,\n });\n\n return rows.map(toNotification);\n}\n\n/**\n * Mark a single notification as read.\n */\nexport async function markRead(walletId: string, notificationId: string): Promise<boolean> {\n const existing = await prisma.notification.findFirst({\n where: { id: notificationId, walletId },\n });\n if (!existing) return false;\n\n await prisma.notification.update({\n where: { id: notificationId },\n data: { read: true },\n });\n\n return true;\n}\n\n/**\n * Mark all notifications as read for a wallet.\n */\nexport async function markAllRead(walletId: string): Promise<number> {\n const result = await prisma.notification.updateMany({\n where: { walletId, read: false },\n data: { read: true },\n });\n\n return result.count;\n}\n\n/**\n * Get count of unread notifications.\n */\nexport async function getUnreadCount(walletId: string): Promise<number> {\n return prisma.notification.count({\n where: { walletId, read: false },\n });\n}\n","/**\n * Server-side event bus for broadcasting real-time agent events via SSE.\n *\n * Singleton pattern using globalThis for Next.js HMR stability.\n * Clients connect via /api/events and receive typed agent events.\n */\n\n/** All event types emitted by the agent system. */\nexport type AgentEventType =\n | \"connected\"\n | \"tool_call_start\"\n | \"tool_call_end\"\n | \"tx_confirmed\"\n | \"spending_limit_hit\"\n | \"agent_error\"\n | \"agent_thinking\"\n | \"agent_lifecycle\"\n | \"notification\"\n | \"fraud_alert\"\n | \"payment_executed\"\n | \"payment_failed\"\n | \"subscription_due\"\n | \"strategy_executed\"\n | \"strategy_failed\"\n | \"emergency_stop\"\n | \"emergency_resume\"\n | \"battle_started\"\n | \"battle_tick\"\n | \"battle_completed\";\n\n/** A typed event emitted to SSE clients. */\nexport interface AgentEvent {\n id: string;\n type: AgentEventType;\n timestamp: string;\n walletId?: string;\n data: Record<string, unknown>;\n}\n\n/** A connected SSE client with its stream controller. */\ninterface SSEClient {\n id: string;\n controller: ReadableStreamDefaultController;\n /** If set, only receive events for this wallet. */\n walletId?: string;\n}\n\nconst encoder = new TextEncoder();\n\nlet nextEventId = 1;\n\n/** Callback for non-SSE listeners (e.g. messaging bridge). */\nexport type EventListener = (event: AgentEvent) => void;\n\nclass EventBus {\n private clients = new Map<string, SSEClient>();\n private listeners: EventListener[] = [];\n\n /** Register a non-SSE listener. Returns a cleanup function. */\n addListener(fn: EventListener): () => void {\n this.listeners.push(fn);\n return () => {\n this.listeners = this.listeners.filter((l) => l !== fn);\n };\n }\n\n /** Register a new SSE client. Returns a cleanup function. */\n addClient(client: SSEClient): () => void {\n this.clients.set(client.id, client);\n return () => {\n this.clients.delete(client.id);\n };\n }\n\n /** Get count of connected clients. */\n get clientCount(): number {\n return this.clients.size;\n }\n\n /** Emit a typed event to all matching clients. Returns the full event. */\n emit(partial: Omit<AgentEvent, \"id\" | \"timestamp\">): AgentEvent {\n const event: AgentEvent = {\n ...partial,\n id: String(nextEventId++),\n timestamp: new Date().toISOString(),\n };\n\n const encoded = encoder.encode(`data: ${JSON.stringify(event)}\\n\\n`);\n\n for (const [clientId, client] of this.clients) {\n // Skip if client filters by walletId and event is for a different wallet\n if (\n client.walletId &&\n event.walletId &&\n client.walletId !== event.walletId\n ) {\n continue;\n }\n\n try {\n client.controller.enqueue(encoded);\n } catch {\n // Client disconnected — remove silently\n this.clients.delete(clientId);\n }\n }\n\n // Notify non-SSE listeners (e.g. messaging bridge)\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch {\n // Listener errors must not break event dispatch\n }\n }\n\n return event;\n }\n\n /** Send a keep-alive comment to all connected clients. */\n ping(): void {\n const encoded = encoder.encode(`: ping\\n\\n`);\n\n for (const [clientId, client] of this.clients) {\n try {\n client.controller.enqueue(encoded);\n } catch {\n this.clients.delete(clientId);\n }\n }\n }\n\n /** Remove all clients and listeners — only for tests. */\n _reset(): void {\n this.clients.clear();\n this.listeners = [];\n nextEventId = 1;\n }\n}\n\n// Singleton via globalThis so HMR doesn't create multiple instances\nconst GLOBAL_KEY = \"__agent_event_bus__\" as const;\ntype GlobalWithBus = typeof globalThis & { [GLOBAL_KEY]?: EventBus };\n\nfunction getEventBus(): EventBus {\n const g = globalThis as GlobalWithBus;\n if (!g[GLOBAL_KEY]) {\n g[GLOBAL_KEY] = new EventBus();\n }\n return g[GLOBAL_KEY];\n}\n\nexport const eventBus = getEventBus();\n","/**\n * MCP Tool definitions and handlers for stng_defi_wallets.\n *\n * Wraps existing src/lib/ modules to expose 18 DeFi, wallet, and agent\n * management tools via the Model Context Protocol.\n */\n\nimport { WalletManager } from \"../lib/wallet-manager.js\";\nimport {\n readAuditLog,\n verifyAuditIntegrity,\n type AuditQueryOptions,\n} from \"../lib/audit-log.js\";\nimport {\n getSpendingLimits,\n setSpendingLimits,\n getSpendingSummary,\n checkSpendingLimit,\n} from \"../lib/spending-limits.js\";\nimport {\n getAgentLifecycle,\n setAgentLifecycle,\n isAgentExecutable,\n type LifecycleState,\n} from \"../lib/agent-lifecycle.js\";\nimport { buildAgentMonitor } from \"../lib/agent-monitor.js\";\nimport { buildPortfolio } from \"../lib/portfolio.js\";\nimport { readTxLog } from \"../lib/tx-log.js\";\nimport {\n getAlerts,\n getFraudStats,\n} from \"../lib/fraud-monitor.js\";\nimport type { WalletContext } from \"./types.js\";\n\n// ============================================\n// Lazy singleton WalletManager\n// ============================================\n\nlet _manager: WalletManager | null = null;\n\nfunction getManager(ctx: WalletContext): WalletManager {\n if (!_manager) {\n _manager = new WalletManager(ctx.encryptionKey, ctx.rpcUrl);\n }\n return _manager;\n}\n\n// ============================================\n// Tool definitions (MCP schema format)\n// ============================================\n\nexport const TOOLS = [\n // ── Wallet Operations ──\n {\n name: \"stng_wallet_create\",\n description:\n \"Create a new Solana wallet with AES-256-GCM encryption. On devnet, automatically airdrops 1 SOL. Returns wallet ID, public key, and balance.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n label: {\n type: \"string\",\n description: \"Human-readable label for the wallet (e.g., 'trading-bot-1')\",\n },\n },\n required: [],\n },\n },\n {\n name: \"stng_wallet_list\",\n description:\n \"List all wallets managed by this instance. Returns wallet IDs, public keys, labels, and creation dates.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {},\n required: [],\n },\n },\n {\n name: \"stng_wallet_balance\",\n description:\n \"Get the SOL balance of a wallet by its public key (base58). Returns balance in SOL.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n public_key: {\n type: \"string\",\n description: \"Solana public key (base58 encoded)\",\n },\n },\n required: [\"public_key\"],\n },\n },\n {\n name: \"stng_wallet_tokens\",\n description:\n \"Get all SPL token balances for a wallet. Returns mint addresses, balances, and decimals.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n public_key: {\n type: \"string\",\n description: \"Solana public key (base58 encoded)\",\n },\n },\n required: [\"public_key\"],\n },\n },\n {\n name: \"stng_wallet_airdrop\",\n description:\n \"Request a devnet SOL airdrop to a wallet. Only works on devnet/localnet. Default: 1 SOL.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n public_key: {\n type: \"string\",\n description: \"Solana public key (base58 encoded)\",\n },\n amount: {\n type: \"number\",\n description: \"Amount of SOL to airdrop (default: 1)\",\n },\n },\n required: [\"public_key\"],\n },\n },\n\n // ── FROST Threshold Signing ──\n {\n name: \"stng_frost_create\",\n description:\n \"Create a FROST 2-of-3 threshold signing wallet. Uses Shamir secret sharing (RFC 9591) so no single party holds the full key. Returns wallet ID and group public key.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n label: {\n type: \"string\",\n description: \"Label for the FROST wallet\",\n },\n threshold: {\n type: \"number\",\n description: \"Signing threshold (default: 2)\",\n },\n total_shares: {\n type: \"number\",\n description: \"Total key shares (default: 3)\",\n },\n },\n required: [],\n },\n },\n {\n name: \"stng_frost_list\",\n description:\n \"List all FROST threshold wallets with their group public keys, thresholds, and share counts.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {},\n required: [],\n },\n },\n {\n name: \"stng_frost_verify\",\n description:\n \"Verify the integrity of a FROST wallet's key shares. Confirms shares can reconstruct a valid group signature.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n wallet_id: {\n type: \"string\",\n description: \"FROST wallet ID (8-char hex)\",\n },\n },\n required: [\"wallet_id\"],\n },\n },\n\n // ── Agent Lifecycle ──\n {\n name: \"stng_agent_status\",\n description:\n \"Get the lifecycle state (active/paused/terminated) of an agent wallet, including transition history and execution eligibility.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n wallet_id: {\n type: \"string\",\n description: \"Agent wallet ID (8-char hex)\",\n },\n },\n required: [\"wallet_id\"],\n },\n },\n {\n name: \"stng_agent_pause\",\n description:\n \"Pause an active agent. Paused agents cannot execute transactions but retain their wallets and state.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n wallet_id: {\n type: \"string\",\n description: \"Agent wallet ID to pause\",\n },\n reason: {\n type: \"string\",\n description: \"Reason for pausing (logged in audit trail)\",\n },\n },\n required: [\"wallet_id\"],\n },\n },\n {\n name: \"stng_agent_resume\",\n description:\n \"Resume a paused agent, restoring its ability to execute transactions.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n wallet_id: {\n type: \"string\",\n description: \"Agent wallet ID to resume\",\n },\n reason: {\n type: \"string\",\n description: \"Reason for resuming (logged in audit trail)\",\n },\n },\n required: [\"wallet_id\"],\n },\n },\n {\n name: \"stng_agent_monitor\",\n description:\n \"Get fleet-wide agent monitoring dashboard. Shows all agents with balances, statuses, strategies, success rates, and spending summaries.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {},\n required: [],\n },\n },\n\n // ── Spending & Security ──\n {\n name: \"stng_spending_limits\",\n description:\n \"Get or update spending limits for a wallet. Returns per-transaction limit, daily limit, and current usage.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n wallet_id: {\n type: \"string\",\n description: \"Wallet ID (8-char hex)\",\n },\n action: {\n type: \"string\",\n enum: [\"get\", \"set\"],\n description: \"Whether to get current limits or set new ones (default: get)\",\n },\n per_tx_limit: {\n type: \"number\",\n description: \"Max SOL per transaction (only for action=set)\",\n },\n daily_limit: {\n type: \"number\",\n description: \"Max SOL per 24h (only for action=set)\",\n },\n },\n required: [\"wallet_id\"],\n },\n },\n {\n name: \"stng_audit_log\",\n description:\n \"Read the HMAC-chained audit log. Filter by action type, wallet ID, agent ID, or time range. Returns tamper-evident entries newest-first.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n action: {\n type: \"string\",\n description: \"Filter by action type (e.g., 'wallet_create', 'frost_sign', 'agent_tool_call')\",\n },\n wallet_id: {\n type: \"string\",\n description: \"Filter by wallet ID\",\n },\n limit: {\n type: \"number\",\n description: \"Max entries to return (default: 20)\",\n },\n },\n required: [],\n },\n },\n {\n name: \"stng_audit_verify\",\n description:\n \"Verify the integrity of the entire HMAC-chained audit log. Detects any tampering or corruption in the chain.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {},\n required: [],\n },\n },\n\n // ── Portfolio & Analytics ──\n {\n name: \"stng_portfolio\",\n description:\n \"Get a portfolio summary across all wallets. Includes SOL and token balances, USD values (via Jupiter Price API), and allocation breakdown.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {},\n required: [],\n },\n },\n {\n name: \"stng_tx_history\",\n description:\n \"Get recent transaction history for a wallet. Shows tool calls, results, and success/failure status.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n wallet_id: {\n type: \"string\",\n description: \"Wallet ID (8-char hex)\",\n },\n limit: {\n type: \"number\",\n description: \"Max entries (default: 20)\",\n },\n },\n required: [\"wallet_id\"],\n },\n },\n {\n name: \"stng_fraud_alerts\",\n description:\n \"Get fraud detection alerts for a wallet. Monitors for rapid drain, velocity spikes, new program interaction, authority changes, and repeated failures.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n wallet_id: {\n type: \"string\",\n description: \"Wallet ID (8-char hex)\",\n },\n },\n required: [\"wallet_id\"],\n },\n },\n] as const;\n\n// ============================================\n// Tool handlers\n// ============================================\n\nexport async function handleTool(\n name: string,\n args: Record<string, unknown>,\n ctx: WalletContext,\n): Promise<unknown> {\n const manager = getManager(ctx);\n\n switch (name) {\n // ── Wallet Operations ──\n case \"stng_wallet_create\": {\n const label = args.label as string | undefined;\n const wallet = await manager.createWalletWithAirdrop(label, 1, {\n isAgent: true,\n });\n return {\n wallet_id: wallet.wallet.id,\n public_key: wallet.wallet.publicKey,\n label: wallet.wallet.label,\n created_at: wallet.wallet.createdAt,\n airdrop: wallet.airdrop\n ? { signature: wallet.airdrop.signature, amount: wallet.airdrop.amount }\n : null,\n };\n }\n\n case \"stng_wallet_list\": {\n const wallets = await manager.listWallets();\n return {\n count: wallets.length,\n wallets: wallets.map((w) => ({\n id: w.id,\n public_key: w.publicKey,\n label: w.label ?? null,\n created_at: w.createdAt,\n is_agent: w.isAgent ?? false,\n })),\n };\n }\n\n case \"stng_wallet_balance\": {\n const pk = args.public_key as string;\n const balance = await manager.getBalance(pk);\n return { public_key: pk, balance_sol: balance };\n }\n\n case \"stng_wallet_tokens\": {\n const pk = args.public_key as string;\n const tokens = await manager.getTokenBalances(pk);\n return { public_key: pk, tokens };\n }\n\n case \"stng_wallet_airdrop\": {\n const pk = args.public_key as string;\n const amount = (args.amount as number) ?? 1;\n const sig = await manager.requestAirdrop(pk, amount);\n return { public_key: pk, amount, signature: sig };\n }\n\n // ── FROST Threshold Signing ──\n case \"stng_frost_create\": {\n const label = args.label as string | undefined;\n const threshold = (args.threshold as number) ?? 2;\n const totalShares = (args.total_shares as number) ?? 3;\n const wallet = await manager.createFrostWallet({\n threshold,\n totalShares,\n label,\n });\n return {\n wallet_id: wallet.id,\n group_public_key: wallet.groupPublicKey,\n threshold: wallet.threshold,\n total_shares: wallet.totalShares,\n label: wallet.label ?? null,\n created_at: wallet.createdAt,\n };\n }\n\n case \"stng_frost_list\": {\n const wallets = await manager.listFrostWallets();\n return {\n count: wallets.length,\n wallets: wallets.map((w) => ({\n id: w.id,\n group_public_key: w.groupPublicKey,\n threshold: w.threshold,\n total_shares: w.totalShares,\n label: w.label ?? null,\n created_at: w.createdAt,\n })),\n };\n }\n\n case \"stng_frost_verify\": {\n const walletId = args.wallet_id as string;\n const valid = await manager.verifyFrostShares(walletId);\n return {\n wallet_id: walletId,\n shares_valid: valid,\n message: valid\n ? \"All key shares are intact and can reconstruct a valid group signature.\"\n : \"Share verification failed — shares may be corrupted.\",\n };\n }\n\n // ── Agent Lifecycle ──\n case \"stng_agent_status\": {\n const walletId = args.wallet_id as string;\n const lifecycle = await getAgentLifecycle(walletId);\n const executable = await isAgentExecutable(walletId);\n return {\n wallet_id: walletId,\n state: lifecycle.state,\n updated_at: lifecycle.updatedAt,\n can_execute: executable.allowed,\n reason: lifecycle.reason ?? null,\n transitions: lifecycle.transitions,\n };\n }\n\n case \"stng_agent_pause\": {\n const walletId = args.wallet_id as string;\n const reason = args.reason as string | undefined;\n const result = await setAgentLifecycle(walletId, \"paused\" as LifecycleState, reason);\n return {\n wallet_id: walletId,\n state: result.state,\n updated_at: result.updatedAt,\n reason: result.reason ?? null,\n };\n }\n\n case \"stng_agent_resume\": {\n const walletId = args.wallet_id as string;\n const reason = args.reason as string | undefined;\n const result = await setAgentLifecycle(walletId, \"active\" as LifecycleState, reason);\n return {\n wallet_id: walletId,\n state: result.state,\n updated_at: result.updatedAt,\n reason: result.reason ?? null,\n };\n }\n\n case \"stng_agent_monitor\": {\n const data = await buildAgentMonitor(manager);\n return {\n total_agents: data.totalAgents,\n active: data.activeAgents,\n paused: data.pausedAgents,\n terminated: data.terminatedAgents,\n error: data.errorAgents,\n agents: data.agents.map((a) => ({\n id: a.id,\n public_key: a.publicKey,\n label: a.label,\n balance_sol: a.balance,\n status: a.status,\n lifecycle_state: a.lifecycleState,\n strategy: a.strategy,\n total_actions: a.totalActions,\n success_rate: a.successRate,\n last_action: a.lastAction,\n })),\n fetched_at: data.fetchedAt,\n };\n }\n\n // ── Spending & Security ──\n case \"stng_spending_limits\": {\n const walletId = args.wallet_id as string;\n const action = (args.action as string) ?? \"get\";\n\n if (action === \"set\") {\n const config: Record<string, number> = {};\n if (args.per_tx_limit !== undefined)\n config.maxPerTx = args.per_tx_limit as number;\n if (args.daily_limit !== undefined)\n config.maxPerDay = args.daily_limit as number;\n await setSpendingLimits(walletId, config);\n }\n\n const limits = await getSpendingLimits(walletId);\n const summary = await getSpendingSummary(walletId);\n return {\n wallet_id: walletId,\n limits,\n summary,\n };\n }\n\n case \"stng_audit_log\": {\n const opts: AuditQueryOptions = {\n limit: (args.limit as number) ?? 20,\n };\n if (args.action) opts.action = args.action as AuditQueryOptions[\"action\"];\n if (args.wallet_id) opts.walletId = args.wallet_id as string;\n const entries = await readAuditLog(opts);\n return { count: entries.length, entries };\n }\n\n case \"stng_audit_verify\": {\n const result = await verifyAuditIntegrity();\n return {\n ...result,\n message: result.valid\n ? `Audit log integrity verified — ${result.total} entries, chain intact.`\n : `Audit log integrity BROKEN at entry ${result.brokenAt} of ${result.total}.`,\n };\n }\n\n // ── Portfolio & Analytics ──\n case \"stng_portfolio\": {\n const portfolio = await buildPortfolio(manager);\n return portfolio;\n }\n\n case \"stng_tx_history\": {\n const walletId = args.wallet_id as string;\n const limit = (args.limit as number) ?? 20;\n const entries = await readTxLog(walletId, limit);\n return { wallet_id: walletId, count: entries.length, transactions: entries };\n }\n\n case \"stng_fraud_alerts\": {\n const walletId = args.wallet_id as string;\n const alerts = await getAlerts(walletId);\n const stats = await getFraudStats(walletId);\n return {\n wallet_id: walletId,\n alerts,\n stats,\n };\n }\n\n default:\n throw new Error(`Unknown tool: ${name}`);\n }\n}\n","/**\n * MCP Prompt definitions for stng_defi_wallets.\n *\n * Prompts guide LLMs through common multi-step workflows\n * using the available MCP tools.\n */\n\n// ============================================\n// Prompt definitions\n// ============================================\n\nexport const PROMPTS = [\n {\n name: \"portfolio_overview\",\n description:\n \"Get a comprehensive portfolio overview across all wallets with USD values, allocations, and balances.\",\n arguments: [],\n },\n {\n name: \"security_audit\",\n description:\n \"Run a full security audit: verify audit log integrity, check spending limits, review fraud alerts, and FROST wallet status.\",\n arguments: [\n {\n name: \"wallet_id\",\n description: \"Specific wallet to audit (optional — omit for fleet-wide)\",\n required: false,\n },\n ],\n },\n {\n name: \"create_agent_wallet\",\n description:\n \"Guided workflow to create a new agent wallet with spending limits and optional FROST threshold protection.\",\n arguments: [\n {\n name: \"label\",\n description: \"Name for the new agent (e.g., 'trading-bot-alpha')\",\n required: true,\n },\n {\n name: \"use_frost\",\n description: \"Whether to create a FROST threshold wallet (yes/no, default: no)\",\n required: false,\n },\n ],\n },\n {\n name: \"fleet_health\",\n description:\n \"Check the health of all agents in the fleet: statuses, balances, success rates, and spending.\",\n arguments: [],\n },\n {\n name: \"investigate_wallet\",\n description:\n \"Deep investigation of a specific wallet: balance, tokens, transaction history, spending, fraud alerts, and lifecycle state.\",\n arguments: [\n {\n name: \"wallet_id\",\n description: \"The wallet ID to investigate\",\n required: true,\n },\n ],\n },\n];\n\n// ============================================\n// Prompt content generators\n// ============================================\n\nexport function getPromptContent(\n name: string,\n args: Record<string, string>,\n): string {\n switch (name) {\n case \"portfolio_overview\":\n return `Please give me a comprehensive portfolio overview.\n\nSteps:\n1. Use stng_portfolio to get all wallet balances, token holdings, and USD values\n2. Use stng_wallet_list to get wallet labels and metadata\n3. Present a clear summary including:\n - Total portfolio value in USD\n - Per-wallet breakdown (label, SOL balance, token balances, USD value)\n - Asset allocation percentages\n - Any wallets with zero balance that may need funding`;\n\n case \"security_audit\": {\n const walletClause = args.wallet_id\n ? `Focus on wallet ${args.wallet_id}.`\n : \"Audit all wallets fleet-wide.\";\n return `Please run a comprehensive security audit. ${walletClause}\n\nSteps:\n1. Use stng_audit_verify to check HMAC chain integrity of the audit log\n2. Use stng_audit_log to review recent security-relevant events (wallet_create, frost_sign, spending_limit_exceeded)\n3. Use stng_frost_list to check FROST wallet status and verify shares with stng_frost_verify\n4. ${args.wallet_id ? `Use stng_spending_limits for wallet ${args.wallet_id}` : \"Use stng_agent_monitor to check all agents\"}\n5. ${args.wallet_id ? `Use stng_fraud_alerts for wallet ${args.wallet_id}` : \"Check fraud alerts for any flagged wallets\"}\n\nReport:\n- Audit log integrity status (intact/broken)\n- Total audit entries and recent activity\n- FROST wallet health\n- Spending limit compliance\n- Any fraud alerts or anomalies\n- Security recommendations`;\n }\n\n case \"create_agent_wallet\": {\n const label = args.label || \"new-agent\";\n const useFrost = args.use_frost?.toLowerCase() === \"yes\";\n return `Please create a new agent wallet named \"${label}\".\n\nSteps:\n1. ${useFrost ? \"Use stng_frost_create to create a FROST 2-of-3 threshold wallet\" : \"Use stng_wallet_create to create a standard wallet\"} with label \"${label}\"\n2. Use stng_spending_limits with action=set to configure:\n - per_tx_limit: 0.5 SOL (conservative start)\n - daily_limit: 2 SOL\n3. Use stng_agent_status to confirm the agent is in \"active\" state\n4. Use stng_wallet_balance to confirm initial balance\n\nReport the new agent's:\n- Wallet ID\n- Public key\n- ${useFrost ? \"FROST group public key and threshold\" : \"Balance\"}\n- Spending limits\n- Lifecycle state`;\n }\n\n case \"fleet_health\":\n return `Please check the health of all agents in the fleet.\n\nSteps:\n1. Use stng_agent_monitor to get the full fleet dashboard\n2. For any agents in \"error\" or \"paused\" state, use stng_agent_status for details\n3. Use stng_portfolio to check fleet-wide balances\n\nReport:\n- Total agents (active / paused / terminated / error)\n- Per-agent health summary (balance, success rate, last action, strategy)\n- Any agents that need attention (low balance, high failure rate, paused)\n- Fleet-wide statistics (total value, average success rate)`;\n\n case \"investigate_wallet\": {\n const walletId = args.wallet_id || \"[WALLET_ID]\";\n return `Please do a deep investigation of wallet ${walletId}.\n\nSteps:\n1. Use stng_agent_status to check lifecycle state and transition history\n2. Use stng_spending_limits to review spending configuration and usage\n3. Use stng_tx_history to review recent transactions\n4. Use stng_fraud_alerts to check for any security alerts\n5. Use stng_audit_log filtered by wallet_id=${walletId} to review audit trail\n\nReport:\n- Wallet state (active/paused/terminated) and why\n- Spending: limits vs actual usage, any violations\n- Recent transactions: success/failure patterns, tools used\n- Fraud alerts: any triggered rules, severity\n- Audit trail: key events timeline\n- Recommendations for this wallet`;\n }\n\n default:\n return `Unknown prompt: ${name}`;\n }\n}\n"],"mappings":"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","names":["totalValueUsd","crypto","crypto"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agentlili",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "AgentLili — Agentic DeFi wallet for Solana. CLI, MCP server, and web dashboard.",
|
|
6
|
+
"keywords": ["solana", "defi", "wallet", "agent", "cli", "mcp", "agentlili"],
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"bin": {
|
|
9
|
+
"agentlili": "dist/cli.js",
|
|
10
|
+
"agentlili-mcp": "dist/mcp-server.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"mcp": "tsx src/mcp/server.ts",
|
|
18
|
+
"dev": "next dev",
|
|
19
|
+
"build": "next build",
|
|
20
|
+
"build:cli": "tsup",
|
|
21
|
+
"prepublishOnly": "tsup",
|
|
22
|
+
"start": "next start",
|
|
23
|
+
"lint": "next lint",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"test:watch": "vitest",
|
|
27
|
+
"test:integration": "RUN_INTEGRATION=1 vitest run --config vitest.integration.config.ts",
|
|
28
|
+
"test:e2e": "playwright test",
|
|
29
|
+
"test:e2e:headed": "playwright test --headed",
|
|
30
|
+
"test:e2e:ui": "playwright test --ui",
|
|
31
|
+
"demo": "tsx scripts/demo.ts",
|
|
32
|
+
"demo:defi": "tsx scripts/demo-defi.ts",
|
|
33
|
+
"demo:multi": "tsx scripts/demo-multi-agent.ts",
|
|
34
|
+
"stress-test": "tsx scripts/stress-test.ts",
|
|
35
|
+
"headless-demo": "tsx scripts/headless-demo.ts",
|
|
36
|
+
"frost-demo": "tsx scripts/frost-demo.ts",
|
|
37
|
+
"fresh-start": "tsx scripts/fresh-start.ts",
|
|
38
|
+
"check-env": "tsx scripts/check-env.ts",
|
|
39
|
+
"e2e-demo": "tsx scripts/e2e-demo.ts",
|
|
40
|
+
"ralph": "bash scripts/ralph.sh",
|
|
41
|
+
"cli": "tsx src/cli/index.ts",
|
|
42
|
+
"surfpool:start": "surfpool start --network mainnet --no-tui --no-studio --daemon",
|
|
43
|
+
"surfpool:stop": "pkill -f surfpool || true",
|
|
44
|
+
"test:surfpool": "vitest run --config vitest.surfpool.config.ts"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@ai-sdk/anthropic": "^3.0.0",
|
|
48
|
+
"@ai-sdk/groq": "^3.0.29",
|
|
49
|
+
"@ai-sdk/openai": "^3.0.0",
|
|
50
|
+
"@ai-sdk/react": "^3.0.0",
|
|
51
|
+
"@dagrejs/dagre": "^2.0.4",
|
|
52
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
53
|
+
"@prisma/adapter-pg": "^7.4.2",
|
|
54
|
+
"@solana-agent-kit/plugin-defi": "^2.0.8",
|
|
55
|
+
"@solana-agent-kit/plugin-token": "^2.0.9",
|
|
56
|
+
"@solana-program/compute-budget": "^0.14.0",
|
|
57
|
+
"@solana-program/token": "^0.12.0",
|
|
58
|
+
"@solana/kit": "^6.1.0",
|
|
59
|
+
"@solana/kit-plugin-instruction-plan": "^0.6.0",
|
|
60
|
+
"@solana/kit-plugin-payer": "^0.6.0",
|
|
61
|
+
"@solana/kit-plugin-rpc": "^0.6.0",
|
|
62
|
+
"@solana/kora": "^0.1.3",
|
|
63
|
+
"@solana/spl-token": "^0.4.12",
|
|
64
|
+
"@solana/web3.js": "^1.98.0",
|
|
65
|
+
"@substrate-system/frost": "^0.0.9",
|
|
66
|
+
"@xyflow/react": "^12.10.1",
|
|
67
|
+
"ai": "^6.0.0",
|
|
68
|
+
"bs58": "^6.0.0",
|
|
69
|
+
"dotenv": "^17.3.1",
|
|
70
|
+
"grammy": "^1.41.1",
|
|
71
|
+
"next": "^15.1.0",
|
|
72
|
+
"next-auth": "5.0.0-beta.30",
|
|
73
|
+
"pg": "^8.20.0",
|
|
74
|
+
"react": "^19.0.0",
|
|
75
|
+
"react-dom": "^19.0.0",
|
|
76
|
+
"react-markdown": "^10.1.0",
|
|
77
|
+
"remark-gfm": "^4.0.1",
|
|
78
|
+
"solana-agent-kit": "^2.0.10",
|
|
79
|
+
"tweetnacl": "^1.0.3",
|
|
80
|
+
"zod": "^3.24.0"
|
|
81
|
+
},
|
|
82
|
+
"pnpm": {
|
|
83
|
+
"patchedDependencies": {
|
|
84
|
+
"@coral-xyz/anchor@0.31.0": "patches/@coral-xyz__anchor@0.31.0.patch",
|
|
85
|
+
"@meteora-ag/dlmm@1.9.3": "patches/@meteora-ag__dlmm@1.9.3.patch"
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
"devDependencies": {
|
|
89
|
+
"@eslint/eslintrc": "latest",
|
|
90
|
+
"@playwright/test": "^1.58.2",
|
|
91
|
+
"@prisma/client": "^7.4.2",
|
|
92
|
+
"@tailwindcss/postcss": "^4.0.0",
|
|
93
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
94
|
+
"@testing-library/react": "^16.3.2",
|
|
95
|
+
"@types/node": "^22.0.0",
|
|
96
|
+
"@types/pg": "^8.18.0",
|
|
97
|
+
"@types/react": "^19.0.0",
|
|
98
|
+
"@types/react-dom": "^19.0.0",
|
|
99
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
100
|
+
"eslint": "^9.0.0",
|
|
101
|
+
"eslint-config-next": "^15.1.0",
|
|
102
|
+
"jsdom": "^28.1.0",
|
|
103
|
+
"postcss": "^8.5.0",
|
|
104
|
+
"prisma": "^7.4.2",
|
|
105
|
+
"tailwindcss": "^4.0.0",
|
|
106
|
+
"tsup": "^8.5.1",
|
|
107
|
+
"tsx": "^4.19.0",
|
|
108
|
+
"typescript": "^5.7.0",
|
|
109
|
+
"vitest": "^4.0.18"
|
|
110
|
+
}
|
|
111
|
+
}
|