@aiaiaichain/agent 0.1.3 → 0.1.5

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.
Files changed (68) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.js +274 -26
  3. package/dist/core/ChainConfig.js +1 -1
  4. package/dist/core/SystemMonitor.d.ts +32 -0
  5. package/dist/core/SystemMonitor.js +89 -0
  6. package/dist/index.d.ts +17 -2
  7. package/dist/index.js +18 -1
  8. package/dist/models/ModelRegistry.js +12 -4
  9. package/dist/runner/AgentRunner.d.ts +2 -0
  10. package/dist/runner/AgentRunner.js +18 -1
  11. package/dist/runner/ModelClient.js +109 -48
  12. package/dist/session/SessionManager.d.ts +1 -0
  13. package/dist/session/SessionManager.js +8 -2
  14. package/dist/session/SessionStore.d.ts +45 -0
  15. package/dist/session/SessionStore.js +128 -0
  16. package/dist/tools/CrossTools.d.ts +52 -0
  17. package/dist/tools/CrossTools.js +190 -0
  18. package/dist/tools/MarketSentiment.js +22 -13
  19. package/dist/tools/NewsSentiment.js +9 -3
  20. package/dist/tools/PriceFeed.js +11 -4
  21. package/dist/tools/TechnicalAnalysis.js +2 -1
  22. package/dist/tools/TokenCalendar.d.ts +24 -0
  23. package/dist/tools/TokenCalendar.js +81 -0
  24. package/dist/tools/TokenSecurityScanner.d.ts +22 -0
  25. package/dist/tools/TokenSecurityScanner.js +102 -0
  26. package/dist/tools/TransactionSim.d.ts +17 -0
  27. package/dist/tools/TransactionSim.js +78 -0
  28. package/dist/tui/App.d.ts +4 -3
  29. package/dist/tui/App.js +371 -118
  30. package/dist/tui/REPL.d.ts +2 -1
  31. package/dist/tui/REPL.js +190 -16
  32. package/dist/tui/Sparkline.d.ts +21 -0
  33. package/dist/tui/Sparkline.js +44 -0
  34. package/dist/tui/StatusBar.d.ts +5 -1
  35. package/dist/tui/StatusBar.js +6 -4
  36. package/dist/tui/ThemePresets.d.ts +25 -0
  37. package/dist/tui/ThemePresets.js +117 -0
  38. package/dist/util/clipboard.d.ts +9 -0
  39. package/dist/util/clipboard.js +26 -0
  40. package/dist/util/commandSuggest.d.ts +7 -0
  41. package/dist/util/commandSuggest.js +44 -0
  42. package/dist/util/confirmation.d.ts +6 -0
  43. package/dist/util/confirmation.js +16 -0
  44. package/dist/util/errorHandler.d.ts +3 -0
  45. package/dist/util/errorHandler.js +28 -0
  46. package/dist/util/logger.d.ts +11 -0
  47. package/dist/util/logger.js +43 -0
  48. package/dist/util/processManager.d.ts +5 -0
  49. package/dist/util/processManager.js +39 -0
  50. package/dist/util/resilientFetch.d.ts +21 -0
  51. package/dist/util/resilientFetch.js +94 -0
  52. package/dist/util/responseCache.d.ts +27 -0
  53. package/dist/util/responseCache.js +54 -0
  54. package/dist/util/safeLog.d.ts +4 -5
  55. package/dist/util/safeLog.js +68 -30
  56. package/dist/util/scheduler.d.ts +14 -0
  57. package/dist/util/scheduler.js +75 -0
  58. package/dist/util/webhooks.d.ts +9 -0
  59. package/dist/util/webhooks.js +75 -0
  60. package/dist/wallet/ActionFeed.js +12 -4
  61. package/dist/wallet/AgentWallet.d.ts +2 -0
  62. package/dist/wallet/AgentWallet.js +31 -5
  63. package/dist/wallet/ProfitTracker.d.ts +30 -0
  64. package/dist/wallet/ProfitTracker.js +93 -0
  65. package/docs/COMMANDS.md +40 -9
  66. package/docs/README.md +2 -0
  67. package/package.json +5 -3
  68. package/scripts/postinstall.js +34 -0
package/dist/tui/App.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  /**
3
- * App — root Ink component. Multi-pane TUI with sidebar (agent wallet, actions, fees, news)
4
- * and main REPL area.
3
+ * App — root Ink component. Multi-pane TUI with sidebar, system monitor,
4
+ * session persistence, and full GMGN command access.
5
5
  */
6
6
  import { useState, useCallback, useEffect, useRef, useMemo } from "react";
7
7
  import { Box, Text, useApp, useInput } from "ink";
@@ -16,7 +16,6 @@ import { makeTheme, T, AIAIAI_COLORS } from "./theme.js";
16
16
  import { AgentRunner } from "../runner/AgentRunner.js";
17
17
  import { SessionManager } from "../session/SessionManager.js";
18
18
  import { resolveModelConfig } from "../runner/ModelClient.js";
19
- import { modelRegistry } from "../models/ModelRegistry.js";
20
19
  import { priceFeed } from "../tools/PriceFeed.js";
21
20
  import { getNewsTool, getNewsParams, newsFeed } from "../tools/NewsSentiment.js";
22
21
  import { analyzeTAParams, getCandlesParams, getCandlesTool, fullAnalysis } from "../tools/TechnicalAnalysis.js";
@@ -25,9 +24,19 @@ import { contextStore } from "../session/ContextStore.js";
25
24
  import { goalManager } from "../session/GoalManager.js";
26
25
  import { memoryStore } from "../session/MemoryStore.js";
27
26
  import { agentScheduler } from "../scheduler/AgentScheduler.js";
27
+ import { sessionStore } from "../session/SessionStore.js";
28
28
  import { agentWallet, ACTION_WALLET, DEPOSIT_WALLET, SIGNER } from "../wallet/AgentWallet.js";
29
29
  import { actionFeed } from "../wallet/ActionFeed.js";
30
30
  import { gmgnTool, gmgnToolParams, gmgnHelp, gmgnStatus, gmgnMarketTool, gmgnMarketToolParams } from "../tools/GmgnIntegration.js";
31
+ import { watchTokenTool, watchTokenParams, removeWatchTool, removeWatchParams, listWatchTool, listWatchParams, addAlertTool, addAlertParams, checkAlertsTool, checkAlertsParams, compareTokensTool, compareTokensParams, portfolioTool, portfolioParams, getWatchList as _getWatchList, checkAlerts as _checkAlerts, } from "../tools/CrossTools.js";
32
+ import { systemMonitor } from "../core/SystemMonitor.js";
33
+ import { Sparkline, PriceHistory } from "./Sparkline.js";
34
+ import { launchCalendarTool, getLaunchSummary } from "../tools/TokenCalendar.js";
35
+ import { simulateTxTool } from "../tools/TransactionSim.js";
36
+ import { getPresetColors, listThemes, setTheme as setThemePreset } from "./ThemePresets.js";
37
+ import { copyToClipboard, clipboardSupported } from "../util/clipboard.js";
38
+ import { suggestCommands } from "../util/commandSuggest.js";
39
+ import { notify as notifyWebhook, hasWebhooks } from "../util/webhooks.js";
31
40
  function getContextBar(session) {
32
41
  if (!session)
33
42
  return { pct: 0, bar: "░".repeat(20), color: AIAIAI_COLORS.dim };
@@ -95,9 +104,16 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
95
104
  const [fees, setFees] = useState({ buyFees: 0, burnFees: 0, total: 0 });
96
105
  const [showModelSelector, setShowModelSelector] = useState(false);
97
106
  const [modelSelectorInitialQuery, setModelSelectorInitialQuery] = useState("");
107
+ const [cpu, setCpu] = useState(0);
108
+ const [ram, setRam] = useState(0);
109
+ const [uptime, setUptime] = useState("0m");
110
+ const [apiCalls, setApiCalls] = useState(0);
111
+ const [watchList, setWatchList] = useState([]);
112
+ const priceHistoryRef = useRef(new PriceHistory(40));
98
113
  const runnerRef = useRef(null);
99
114
  const sessionRef = useRef(null);
100
115
  const sessionCtxRef = useRef(null);
116
+ const messagesRef = useRef([]);
101
117
  const theme = makeTheme();
102
118
  let modelName = "no-model";
103
119
  try {
@@ -105,7 +121,11 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
105
121
  }
106
122
  catch { /* shown via banner */ }
107
123
  const push = useCallback((msg) => {
108
- setMessages(prev => [...prev, { ...msg, id: nextId(), ts: Date.now() }]);
124
+ setMessages(prev => {
125
+ const next = [...prev, { ...msg, id: nextId(), ts: Date.now() }];
126
+ messagesRef.current = next;
127
+ return next;
128
+ });
109
129
  }, []);
110
130
  const notify = useCallback((content) => { push({ role: "notify", content }); }, [push]);
111
131
  const setStatus = useCallback((key, value) => {
@@ -120,6 +140,7 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
120
140
  },
121
141
  theme,
122
142
  };
143
+ // ── Register ALL tools ──────────────────────────────────────────────────
123
144
  function registerBuiltinTools() {
124
145
  if (!modelReg)
125
146
  return;
@@ -130,11 +151,11 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
130
151
  if (costTracker) {
131
152
  registry.addTool({ name: "cost_report", label: "Cost Report", description: "Show session and lifetime token usage.", parameters: costTracker.costReportParams, execute: () => costTracker.costReportTool() });
132
153
  }
133
- registry.addTool({ name: "get_aiaiai_price", label: "AIAIAI Price", description: "Get the current $AIAIAI token price, liquidity, market cap, and volume from DexScreener.", parameters: priceFeed.getAiaiaiPriceParams, execute: () => priceFeed.getAiaiaiPriceTool() });
134
- registry.addTool({ name: "get_token_price", label: "Token Price", description: "Get price data for any Solana token by address via DexScreener.", parameters: priceFeed.getTokenPriceParams, execute: (id, p) => priceFeed.getTokenPriceTool(id, p) });
135
- registry.addTool({ name: "get_news", label: "Get News", description: "Crypto news headlines with sentiment scoring.", parameters: getNewsParams, execute: (id, p) => getNewsTool(id, p) });
136
- registry.addTool({ name: "get_candles", label: "Get Candles", description: "Fetch OHLCV candlestick data from Binance and run technical analysis.", parameters: getCandlesParams, execute: (id, p) => getCandlesTool(id, p) });
137
- registry.addTool({ name: "analyze_ta", label: "Technical Analysis", description: "Run RSI, MACD, Bollinger, ATR on a price array.", parameters: analyzeTAParams, execute: async (_id, p) => {
154
+ registry.addTool({ name: "get_aiaiai_price", label: "AIAIAI Price", description: "Get $AIAIAI price, liquidity, MCap, volume.", parameters: priceFeed.getAiaiaiPriceParams, execute: () => priceFeed.getAiaiaiPriceTool() });
155
+ registry.addTool({ name: "get_token_price", label: "Token Price", description: "Get any Solana token price.", parameters: priceFeed.getTokenPriceParams, execute: (id, p) => priceFeed.getTokenPriceTool(id, p) });
156
+ registry.addTool({ name: "get_news", label: "Get News", description: "Crypto news with sentiment.", parameters: getNewsParams, execute: (id, p) => getNewsTool(id, p) });
157
+ registry.addTool({ name: "get_candles", label: "Get Candles", description: "OHLCV from Binance + TA.", parameters: getCandlesParams, execute: (id, p) => getCandlesTool(id, p) });
158
+ registry.addTool({ name: "analyze_ta", label: "Technical Analysis", description: "RSI, MACD, Bollinger, ATR.", parameters: analyzeTAParams, execute: async (_id, p) => {
138
159
  const closes = p.prices;
139
160
  const candles = closes.map((c, i) => ({ timestamp: 0, open: c, high: p.highs?.[i] ?? c, low: p.lows?.[i] ?? c, close: c, volume: p.volumes?.[i] ?? 0 }));
140
161
  const results = fullAnalysis(candles);
@@ -146,45 +167,60 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
146
167
  }).join("\n");
147
168
  return { content: [{ type: "text", text: `Technical Analysis:\n${text}` }], details: { results, overall_signal: summary?.signal } };
148
169
  } });
149
- registry.addTool({ name: "get_fear_greed", label: "Fear & Greed", description: "Crypto Fear & Greed Index with 7-day history.", parameters: fearGreedParams, execute: () => getFearGreedTool() });
150
- registry.addTool({ name: "get_funding_rates", label: "Funding Rates", description: "Binance perpetual funding rates.", parameters: fundingRatesParams, execute: (id, p) => getFundingRatesTool(id, p) });
151
- registry.addTool({ name: "get_btc_mempool", label: "BTC Mempool", description: "Bitcoin mempool stats and fee rates.", parameters: btcMempoolParams, execute: () => getBtcMempoolTool() });
152
- registry.addTool({ name: "get_defi_tvl", label: "DeFi TVL", description: "DeFiLlama TVL by chain or all chains.", parameters: defiTvlParams, execute: (id, p) => getDefiTvlTool(id, p) });
153
- registry.addTool({ name: "get_solana_stats", label: "Solana Stats", description: "Solana network epoch, slot, and SOL price.", parameters: solanaStatsParams, execute: () => getSolanaStatsTool() });
170
+ registry.addTool({ name: "get_fear_greed", label: "Fear & Greed", description: "Crypto Fear & Greed Index.", parameters: fearGreedParams, execute: () => getFearGreedTool() });
171
+ registry.addTool({ name: "get_funding_rates", label: "Funding Rates", description: "Binance funding rates.", parameters: fundingRatesParams, execute: (id, p) => getFundingRatesTool(id, p) });
172
+ registry.addTool({ name: "get_btc_mempool", label: "BTC Mempool", description: "BTC mempool stats.", parameters: btcMempoolParams, execute: () => getBtcMempoolTool() });
173
+ registry.addTool({ name: "get_defi_tvl", label: "DeFi TVL", description: "DeFiLlama TVL.", parameters: defiTvlParams, execute: (id, p) => getDefiTvlTool(id, p) });
174
+ registry.addTool({ name: "get_solana_stats", label: "Solana Stats", description: "Solana epoch, slot, SOL price.", parameters: solanaStatsParams, execute: () => getSolanaStatsTool() });
175
+ registry.addTool({ name: "get_agent_balance", label: "Agent Balance", description: "Cold + action wallet balances.", parameters: agentWallet.getAgentBalanceParams, execute: () => agentWallet.getAgentBalanceTool() });
176
+ registry.addTool({ name: "get_deposit_balance", label: "Deposit Balance", description: "Deposit wallet + instructions.", parameters: agentWallet.getDepositBalanceParams, execute: () => agentWallet.getDepositBalanceTool() });
177
+ registry.addTool({ name: "get_actions", label: "Agent Actions", description: "Recent buy/burn actions.", parameters: actionFeed.getActionsParams, execute: (id, p) => actionFeed.getActionsTool(id, p) });
178
+ registry.addTool({ name: "get_fees", label: "Agent Fees", description: "Accumulated fees.", parameters: actionFeed.getFeesParams, execute: () => actionFeed.getFeesTool() });
154
179
  registry.addTool({ name: "read_task_context", label: "Read Task Context", description: "Read saved task context.", parameters: Type.Object({ taskId: Type.String({ description: "Task ID" }) }), execute: (id, p) => contextStore.readContextTool(id, p) });
155
180
  registry.addTool({ name: "list_tasks", label: "List Tasks", description: "List saved task contexts.", parameters: Type.Object({}), execute: () => contextStore.listTasksTool() });
156
- registry.addTool({ name: "set_goal", label: "Set Goal", description: "Set a persistent cross-session goal.", parameters: goalManager.setGoalParams, execute: (id, p) => goalManager.setGoalTool(id, p) });
181
+ registry.addTool({ name: "set_goal", label: "Set Goal", description: "Set a persistent goal.", parameters: goalManager.setGoalParams, execute: (id, p) => goalManager.setGoalTool(id, p) });
157
182
  registry.addTool({ name: "complete_goal", label: "Complete Goal", description: "Mark a goal complete.", parameters: goalManager.completeGoalParams, execute: (id, p) => goalManager.completeGoalTool(id, p) });
158
183
  registry.addTool({ name: "list_goals", label: "List Goals", description: "List goals.", parameters: goalManager.listGoalsParams, execute: (id, p) => goalManager.listGoalsTool(id, p) });
159
184
  registry.addTool({ name: "add_goal_note", label: "Add Goal Note", description: "Add a note to a goal.", parameters: goalManager.addGoalNoteParams, execute: (id, p) => goalManager.addGoalNoteTool(id, p) });
160
- registry.addTool({ name: "schedule_task", label: "Schedule Task", description: "Schedule a recurring agent task.", parameters: agentScheduler.addTaskParams, execute: (id, p) => agentScheduler.addTaskTool(id, p) });
185
+ registry.addTool({ name: "schedule_task", label: "Schedule Task", description: "Schedule a recurring task.", parameters: agentScheduler.addTaskParams, execute: (id, p) => agentScheduler.addTaskTool(id, p) });
161
186
  registry.addTool({ name: "list_schedule", label: "List Schedule", description: "List scheduled tasks.", parameters: agentScheduler.listTasksParams, execute: (id, p) => agentScheduler.listTasksTool(id, p) });
162
187
  registry.addTool({ name: "remove_schedule", label: "Remove Schedule", description: "Remove a scheduled task.", parameters: agentScheduler.removeTaskParams, execute: (id, p) => agentScheduler.removeTaskTool(id, p) });
163
188
  // GMGN tools
164
- registry.addTool({ name: "gmgn", label: "GMGN Token Research", description: "Research tokens via GMGN: price, security, holders, traders, smart money tracking. Sub-commands: token info, token security, token holders, token traders, track kol, track smartmoney, track follow-wallet, track follow-tokens. Usage: gmgn <subcommand> --chain <sol|bsc|base|eth> --address <address>", parameters: gmgnToolParams, execute: (id, p) => gmgnTool(id, p) });
165
- registry.addTool({ name: "gmgn_market", label: "GMGN Market Data", description: "Market data via GMGN: trending tokens, trenches (new launches), kline charts, signals, holders, traders. Usage: gmgn market <trending|trenches|signal|kline <addr>|holders <addr>|traders <addr>>", parameters: gmgnMarketToolParams, execute: (id, p) => gmgnMarketTool(id, p) });
166
- // Wallet tools
167
- registry.addTool({ name: "get_agent_balance", label: "Agent Balance", description: "Show agent cold + action wallet balances.", parameters: agentWallet.getAgentBalanceParams, execute: () => agentWallet.getAgentBalanceTool() });
168
- registry.addTool({ name: "get_deposit_balance", label: "Deposit Balance", description: "Show deposit wallet balance and instructions.", parameters: agentWallet.getDepositBalanceParams, execute: () => agentWallet.getDepositBalanceTool() });
169
- registry.addTool({ name: "get_actions", label: "Agent Actions", description: "Show recent agent buy/burn actions.", parameters: actionFeed.getActionsParams, execute: (id, p) => actionFeed.getActionsTool(id, p) });
170
- registry.addTool({ name: "get_fees", label: "Agent Fees", description: "Show accumulated fees from agent actions.", parameters: actionFeed.getFeesParams, execute: () => actionFeed.getFeesTool() });
189
+ registry.addTool({ name: "gmgn", label: "GMGN Token Research", description: "Full GMGN access: token info, security, holders, traders, tracking, market data.", parameters: gmgnToolParams, execute: (id, p) => gmgnTool(id, p) });
190
+ registry.addTool({ name: "gmgn_market", label: "GMGN Market Data", description: "Market data: trending, trenches, kline, signals.", parameters: gmgnMarketToolParams, execute: (id, p) => gmgnMarketTool(id, p) });
191
+ // Cross tools
192
+ registry.addTool({ name: "watch_token", label: "Watch Token", description: "Add token to watch list.", parameters: watchTokenParams, execute: (id, p) => watchTokenTool(id, p) });
193
+ registry.addTool({ name: "unwatch_token", label: "Unwatch Token", description: "Remove token from watch list.", parameters: removeWatchParams, execute: (id, p) => removeWatchTool(id, p) });
194
+ registry.addTool({ name: "watch_list", label: "Watch List", description: "Show watched tokens.", parameters: listWatchParams, execute: () => listWatchTool() });
195
+ registry.addTool({ name: "add_alert", label: "Price Alert", description: "Set a price alert.", parameters: addAlertParams, execute: (id, p) => addAlertTool(id, p) });
196
+ registry.addTool({ name: "check_alerts", label: "Check Alerts", description: "List active alerts.", parameters: checkAlertsParams, execute: () => checkAlertsTool() });
197
+ registry.addTool({ name: "compare_tokens", label: "Compare Tokens", description: "Side-by-side token comparison.", parameters: compareTokensParams, execute: (id, p) => compareTokensTool(id, p) });
198
+ registry.addTool({ name: "portfolio", label: "Portfolio", description: "Wallet portfolio analysis.", parameters: portfolioParams, execute: (id, p) => portfolioTool(id, p) });
171
199
  }
172
- useEffect(() => { onNotifyReady?.(notify); onStatusReady?.(setStatus); }, []);
173
- useEffect(() => {
174
- onModelSelectorReady?.((initialQuery) => {
175
- setModelSelectorInitialQuery(initialQuery ?? "");
176
- setShowModelSelector(true);
177
- });
178
- }, [onModelSelectorReady]);
200
+ // ── Main effect: setup + intervals ──────────────────────────────────────
179
201
  useEffect(() => {
180
202
  registerBuiltinTools();
181
- // Initial price fetch
182
- priceFeed.getAiaiaiPrice().then(p => {
183
- setAiaiPrice({ priceUsd: p.priceUsd, change: p.priceChange24h, mcap: p.marketCap, liq: p.liquidityUsd });
184
- if (p.priceUsd)
185
- setPriceBadge(`$${parseFloat(p.priceUsd).toFixed(6)}`);
186
- actionFeed.setPrice(parseFloat(p.priceUsd || "0.0004"));
187
- }).catch(() => { });
203
+ // Initial price fetch with retry
204
+ const fetchPriceWithRetry = async (retries = 3, delay = 2000) => {
205
+ for (let i = 0; i < retries; i++) {
206
+ try {
207
+ const p = await priceFeed.getAiaiaiPrice();
208
+ setAiaiPrice({ priceUsd: p.priceUsd, change: p.priceChange24h, mcap: p.marketCap, liq: p.liquidityUsd });
209
+ if (p.priceUsd)
210
+ setPriceBadge(`$${parseFloat(p.priceUsd).toFixed(6)}`);
211
+ actionFeed.setPrice(parseFloat(p.priceUsd || "0.0004"));
212
+ return;
213
+ }
214
+ catch (e) {
215
+ if (i === retries - 1)
216
+ throw e;
217
+ await new Promise(r => setTimeout(r, delay * (i + 1)));
218
+ }
219
+ }
220
+ };
221
+ fetchPriceWithRetry().catch(() => {
222
+ console.error('Initial price fetch failed after retries');
223
+ });
188
224
  // Initial wallet fetches
189
225
  agentWallet.getAll().then(w => {
190
226
  setColdBalance({ sol: w.cold.sol, aiaiai: w.cold.aiaiai, usdc: w.cold.usdc });
@@ -201,8 +237,11 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
201
237
  setSidebarNews(report.items.slice(0, 6).map(i => ({ title: i.title, sentiment: i.sentiment ?? 0, source: i.source })));
202
238
  setNewsBadge(newsFeed.statusBadge());
203
239
  }).catch(() => { });
240
+ // Load watch list
241
+ setWatchList(_getWatchList());
242
+ // Scheduler
204
243
  agentScheduler.start((task) => {
205
- push({ role: "system", content: T.muted(`⏰ Scheduled task: "${task.name}"`) });
244
+ push({ role: "system", content: T.muted(`⏰ Scheduled: "${task.name}"`) });
206
245
  if (!disabled && runnerRef.current) {
207
246
  setDisabled(true);
208
247
  setStreaming("");
@@ -214,9 +253,20 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
214
253
  const priceInterval = setInterval(() => {
215
254
  priceFeed.getAiaiaiPrice().then(p => {
216
255
  setAiaiPrice({ priceUsd: p.priceUsd, change: p.priceChange24h, mcap: p.marketCap, liq: p.liquidityUsd });
217
- if (p.priceUsd)
256
+ if (p.priceUsd) {
218
257
  setPriceBadge(`$${parseFloat(p.priceUsd).toFixed(6)}`);
258
+ priceHistoryRef.current.push(parseFloat(p.priceUsd));
259
+ }
219
260
  actionFeed.setPrice(parseFloat(p.priceUsd || "0.0004"));
261
+ systemMonitor.trackApiCall();
262
+ // Check alerts
263
+ if (p.priceUsd) {
264
+ const triggered = _checkAlerts(parseFloat(p.priceUsd), "AIAIAI");
265
+ for (const alert of triggered) {
266
+ notify(`🔔 Alert triggered: ${alert.type} $${alert.price} — $AIAIAI is now $${p.priceUsd}`);
267
+ notifyWebhook(`🔔 **Alert**: ${alert.type} $${alert.price} — $AIAIAI now $${p.priceUsd}`).catch(() => { });
268
+ }
269
+ }
220
270
  }).catch(() => { });
221
271
  if (costTracker)
222
272
  setCostBadge(costTracker.statusLine());
@@ -240,7 +290,21 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
240
290
  setNewsBadge(newsFeed.statusBadge());
241
291
  }).catch(() => { });
242
292
  }, 300_000);
243
- const saveInterval = setInterval(() => { costTracker?.saveLifetime(); }, 60_000);
293
+ // System monitor interval (10s)
294
+ const sysInterval = setInterval(() => {
295
+ const stats = systemMonitor.getStats();
296
+ setCpu(stats.cpuPercent);
297
+ setRam(stats.ramPercent);
298
+ setUptime(stats.uptime);
299
+ setApiCalls(systemMonitor.resetApiCounter());
300
+ }, 10_000);
301
+ // Session auto-save (30s) — use ref to avoid stale closure
302
+ sessionStore.startAutoSave(() => messagesRef.current);
303
+ sessionStore.setCurrentId(sessionId);
304
+ const saveInterval = setInterval(() => {
305
+ if (messagesRef.current.length > 0)
306
+ sessionStore.save(messagesRef.current);
307
+ }, 30_000);
244
308
  const session = new SessionManager();
245
309
  session.setSystemPrompt(registry.getSystemPrompt() || systemPrompt || "You are AIAIAI Chain Agent.");
246
310
  sessionRef.current = session;
@@ -304,9 +368,13 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
304
368
  if (sessionCtxRef.current)
305
369
  registry.fireHook("session_end", sessionCtxRef.current).catch(() => { });
306
370
  agentScheduler.stop();
371
+ sessionStore.stopAutoSave();
372
+ if (messages.length > 0)
373
+ sessionStore.save(messages);
307
374
  clearInterval(priceInterval);
308
375
  clearInterval(walletInterval);
309
376
  clearInterval(newsInterval);
377
+ clearInterval(sysInterval);
310
378
  clearInterval(saveInterval);
311
379
  costTracker?.saveLifetime();
312
380
  };
@@ -314,10 +382,13 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
314
382
  useInput((_input, key) => {
315
383
  if (key.ctrl && _input === "c") {
316
384
  push({ role: "system", content: T.muted("Goodbye 🤖") });
385
+ if (messages.length > 0)
386
+ sessionStore.save(messages);
317
387
  costTracker?.saveLifetime();
318
388
  setTimeout(exit, 200);
319
389
  }
320
390
  });
391
+ // ── Submit handler ─────────────────────────────────────────────────────
321
392
  const handleSubmit = useCallback(async (raw) => {
322
393
  const input = raw.trim();
323
394
  if (!input)
@@ -326,16 +397,157 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
326
397
  const [cmd, ...rest] = input.slice(1).split(" ");
327
398
  const args = rest.join(" ");
328
399
  if (cmd === "exit" || cmd === "quit") {
329
- push({ role: "system", content: T.muted("Goodbye 🤖") });
400
+ if (messages.length > 0)
401
+ sessionStore.save(messages);
330
402
  costTracker?.saveLifetime();
403
+ push({ role: "system", content: T.muted("Goodbye 🤖") });
331
404
  setTimeout(exit, 200);
332
405
  return;
333
406
  }
334
407
  if (cmd === "help") {
335
- const lines = registry.listCommands().map(([n, d]) => T.accent(`/${n}`.padEnd(16)) + " " + d.description);
336
- notify("Commands:\n/gmgn /gmgnmarket /update /keys /wallet /deposit /burn /actions /fees /price /news /models /cost /goals /schedule /model /clear /exit\n" + (lines.length ? "\n" + lines.join("\n") : ""));
408
+ notify("Commands:\n/token info|security|holders|traders\n/track kol|smartmoney|follow-wallet\n/market kline|trending|trenches|signal\n/watch /unwatch /watchlist /alerts /alert /compare /portfolio\n/keys /wallet /deposit /burn /actions /fees\n/models /cost /goals /schedule /update /diff\n/sessions /resume /memory /clear /exit\n/launches /theme /copy /simulate /explain /webhook /clipboard\n\nTab to auto-complete, ↑↓ for history, Shift+Enter for multi-line");
409
+ return;
410
+ }
411
+ // ── GMGN direct commands ──────────────────────────────────────────
412
+ if (cmd === "token") {
413
+ // /token info --chain sol --address <addr>
414
+ const parts = args.split(" ");
415
+ const subcmd = parts[0];
416
+ if (!subcmd || subcmd === "help") {
417
+ notify("Token commands:\n/token info --chain <sol|bsc|base|eth> --address <addr>\n/token security --chain <chain> --address <addr>\n/token holders --chain <chain> --address <addr>\n/token traders --chain <chain> --address <addr>");
418
+ return;
419
+ }
420
+ const chain = parts[parts.indexOf("--chain") + 1] || "sol";
421
+ const address = parts[parts.indexOf("--address") + 1];
422
+ if (!address && subcmd !== "help") {
423
+ notify(T.error("--address is required"));
424
+ return;
425
+ }
426
+ const result = await gmgnTool("", { subcommand: subcmd, chain, address });
427
+ notify(result.content[0].text);
428
+ return;
429
+ }
430
+ if (cmd === "track") {
431
+ // /track kol --chain sol
432
+ const parts = args.split(" ");
433
+ const subcmd = parts[0];
434
+ if (!subcmd || subcmd === "help") {
435
+ notify("Track commands:\n/track kol --chain <sol|bsc|base|eth>\n/track smartmoney --chain <chain>\n/track follow-wallet --chain <chain>");
436
+ return;
437
+ }
438
+ const chain = parts[parts.indexOf("--chain") + 1] || "sol";
439
+ const result = await gmgnTool("", { subcommand: `track ${subcmd}`, chain, limit: 10 });
440
+ notify(result.content[0].text);
441
+ return;
442
+ }
443
+ if (cmd === "market") {
444
+ // /market kline --chain sol --address <addr> --resolution 1h
445
+ const parts = args.split(" ");
446
+ const subcmd = parts[0];
447
+ if (!subcmd || subcmd === "help") {
448
+ notify("Market commands:\n/market kline --chain <chain> --address <addr> --resolution <1h|4h|1d>\n/market trending --chain <chain> --interval <1h|6h|24h>\n/market trenches --chain <chain> --type <new_creation|near_completion|completed>\n/market signal --chain <sol|bsc>");
449
+ return;
450
+ }
451
+ const chain = parts[parts.indexOf("--chain") + 1] || "sol";
452
+ const address = parts[parts.indexOf("--address") + 1];
453
+ const resolution = parts[parts.indexOf("--resolution") + 1];
454
+ const interval = parts[parts.indexOf("--interval") + 1];
455
+ const trenchesType = parts[parts.indexOf("--type") + 1];
456
+ const toolParams = { subcommand: `market ${subcmd}`, chain };
457
+ if (address)
458
+ toolParams.address = address;
459
+ if (resolution)
460
+ toolParams.resolution = resolution;
461
+ if (interval)
462
+ toolParams.interval = interval;
463
+ if (trenchesType)
464
+ toolParams.type = trenchesType;
465
+ const result = await gmgnTool("", toolParams);
466
+ notify(result.content[0].text);
467
+ return;
468
+ }
469
+ // ── Cross tools ────────────────────────────────────────────────────
470
+ if (cmd === "watch") {
471
+ const result = await watchTokenTool("", { address: args || "AVPJS61gZmWKtaEpb7qYPKo8Fk2xQUsayYQxPiPMpump" });
472
+ setWatchList(_getWatchList());
473
+ notify(result.content[0].text);
474
+ return;
475
+ }
476
+ if (cmd === "unwatch") {
477
+ const result = await removeWatchTool("", { address: args });
478
+ setWatchList(_getWatchList());
479
+ notify(result.content[0].text);
480
+ return;
481
+ }
482
+ if (cmd === "watchlist") {
483
+ const result = await listWatchTool();
484
+ notify(result.content[0].text);
485
+ return;
486
+ }
487
+ if (cmd === "alerts") {
488
+ const result = await checkAlertsTool();
489
+ notify(result.content[0].text);
337
490
  return;
338
491
  }
492
+ if (cmd === "alert") {
493
+ // /alert above 0.001 AIAIAI
494
+ const parts = args.split(" ");
495
+ const type = parts[0];
496
+ const price = parseFloat(parts[1]);
497
+ if (!type || isNaN(price)) {
498
+ notify(T.error("Usage: /alert above|below <price>"));
499
+ return;
500
+ }
501
+ const result = await addAlertTool("", { token: "AIAIAI", type, price });
502
+ notify(result.content[0].text);
503
+ return;
504
+ }
505
+ if (cmd === "compare") {
506
+ // /compare <addr1> <addr2>
507
+ const parts = args.split(" ");
508
+ if (parts.length < 2) {
509
+ notify(T.error("Usage: /compare <address1> <address2>"));
510
+ return;
511
+ }
512
+ const result = await compareTokensTool("", { address1: parts[0], address2: parts[1] });
513
+ notify(result.content[0].text);
514
+ return;
515
+ }
516
+ if (cmd === "portfolio") {
517
+ const addr = args || "A11iZoqEt6hU7HyggqC67ee4AtYmaJjwKCvJLerJRV2J";
518
+ const result = await portfolioTool("", { address: addr });
519
+ notify(result.content[0].text);
520
+ return;
521
+ }
522
+ // ── Session management ─────────────────────────────────────────────
523
+ if (cmd === "sessions") {
524
+ const sessions = sessionStore.list();
525
+ if (sessions.length === 0) {
526
+ notify("No saved sessions.");
527
+ return;
528
+ }
529
+ const lines = sessions.slice(0, 10).map((s, i) => ` ${i + 1}. ${s.title} (${s.messageCount} msgs, ${new Date(s.updatedAt).toLocaleDateString()})`);
530
+ notify(`Recent Sessions:\n${lines.join("\n")}\n\nUse /resume <number> to resume.`);
531
+ return;
532
+ }
533
+ if (cmd === "resume") {
534
+ const sessions = sessionStore.list();
535
+ const idx = parseInt(args) - 1;
536
+ if (isNaN(idx) || idx < 0 || idx >= sessions.length) {
537
+ notify(T.error("Invalid session number."));
538
+ return;
539
+ }
540
+ const session = sessionStore.load(sessions[idx].id);
541
+ if (!session) {
542
+ notify(T.error("Session not found."));
543
+ return;
544
+ }
545
+ setMessages(session.messages);
546
+ sessionStore.setCurrentId(session.id);
547
+ notify(`Resumed: ${session.title} (${session.messages.length} messages)`);
548
+ return;
549
+ }
550
+ // ── Standard commands ───────────────────────────────────────────────
339
551
  if (cmd === "price") {
340
552
  const result = await priceFeed.getAiaiaiPriceTool();
341
553
  notify(result.content[0].text);
@@ -384,20 +596,7 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
384
596
  return;
385
597
  }
386
598
  if (cmd === "burn") {
387
- notify([
388
- "🔥 Burn $AIAIAI",
389
- "",
390
- `Deposit wallet: ${DEPOSIT_WALLET}`,
391
- `Send SOL or USDC here to fuel agent burns.`,
392
- "",
393
- `The agent detects deposits and automatically`,
394
- `sends funds to the action wallet to burn tokens.`,
395
- "",
396
- `Action wallet: ${ACTION_WALLET}`,
397
- `Signer: ${SIGNER}`,
398
- "",
399
- `Burn destination: tokens are removed from circulation.`,
400
- ].join("\n"));
599
+ notify(["🔥 Burn $AIAIAI", "", `Deposit: ${DEPOSIT_WALLET}`, "Send SOL or USDC here.", "", `Action: ${ACTION_WALLET}`, `Signer: ${SIGNER}`].join("\n"));
401
600
  return;
402
601
  }
403
602
  if (cmd === "actions" || cmd === "activity") {
@@ -410,6 +609,10 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
410
609
  notify(result.content[0].text);
411
610
  return;
412
611
  }
612
+ if (cmd === "keys" || cmd === "providers") {
613
+ notify("Switch to CLI for key management:\n aiaiai keys");
614
+ return;
615
+ }
413
616
  if (cmd === "goals" || cmd === "goal") {
414
617
  const subCmd = args.trim().split(" ")[0];
415
618
  if (subCmd === "add") {
@@ -441,29 +644,23 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
441
644
  notify(result.content[0].text);
442
645
  return;
443
646
  }
444
- if (cmd === "approve" || cmd === "y") {
445
- const pending = runnerRef.current?._pendingApproval;
446
- if (pending) {
447
- pending(true);
448
- push({ role: "system", content: T.success("✅ Tool approved.") });
449
- }
450
- else
451
- notify(T.error("No pending approval."));
647
+ if (cmd === "update" || cmd === "upgrade") {
648
+ notify("Switch to CLI:\n aiaiai update");
452
649
  return;
453
650
  }
454
- if (cmd === "deny" || cmd === "n") {
455
- const pending = runnerRef.current?._pendingApproval;
456
- if (pending) {
457
- pending(false);
458
- push({ role: "system", content: T.error("❌ Tool denied.") });
651
+ if (cmd === "memory" && args.trim()) {
652
+ const results = memoryStore.search(args.trim(), 8);
653
+ if (results.length === 0) {
654
+ notify(T.muted("No memories found."));
655
+ return;
459
656
  }
460
- else
461
- notify(T.error("No pending approval."));
657
+ const lines = results.map(r => `[${new Date(r.ts).toLocaleDateString()} ${r.role}] ${r.content.slice(0, 120)}`);
658
+ notify(`Memory: "${args.trim()}"\n${lines.join("\n")}`);
462
659
  return;
463
660
  }
464
661
  if (cmd === "gmgn" || cmd === "gmgnhelp") {
465
662
  const gmgnArgs = rest.join(" ");
466
- if (!gmgnArgs || gmgnArgs === "help" || gmgnArgs === "--help") {
663
+ if (!gmgnArgs || gmgnArgs === "help") {
467
664
  notify(gmgnHelp());
468
665
  return;
469
666
  }
@@ -471,61 +668,116 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
471
668
  notify(gmgnStatus());
472
669
  return;
473
670
  }
474
- if (gmgnArgs.startsWith("setup") || gmgnArgs.startsWith("key")) {
475
- notify("GMGN API key setup. Switch to CLI and run: aiaiaichain gmgn setup");
671
+ if (gmgnArgs.startsWith("setup")) {
672
+ notify("Run in CLI: aiaiai gmgn setup");
476
673
  return;
477
674
  }
478
- // Forward to gmgn tool
479
- const result = await gmgnTool("", { subcommand: gmgnArgs.split(" ")[0], chain: "sol", ...Object.fromEntries(gmgnArgs.split(" ").slice(1).reduce((acc, val, i) => { if (i % 2 === 0)
480
- acc.push([val]);
481
- else
482
- acc[acc.length - 1].push(val); return acc; }, []).map(([k, v]) => [k, v || true])) });
675
+ const result = await gmgnTool("", { subcommand: gmgnArgs.split(" ")[0], chain: "sol" });
483
676
  notify(result.content[0].text);
484
677
  return;
485
678
  }
486
679
  if (cmd === "gmgnmarket" || cmd === "market") {
487
- const query = rest.join(" ");
488
- const result = await gmgnMarketTool("", { query: query || "trending", chain: "sol" });
680
+ const result = await gmgnMarketTool("", { query: args || "trending", chain: "sol" });
489
681
  notify(result.content[0].text);
490
682
  return;
491
683
  }
492
- if (cmd === "update" || cmd === "upgrade") {
493
- notify([
494
- "🤖 AIAIAI Update",
495
- "",
496
- "Switch to CLI to check for updates:",
497
- " aiaiai update",
498
- "",
499
- "This will check npm for the latest version",
500
- "and install it if available.",
501
- ].join("\n"));
684
+ // ── New feature commands ─────────────────────────────────────────
685
+ if (cmd === "launches" || cmd === "calendar") {
686
+ const result = await launchCalendarTool("", { chain: args || "solana", limit: 15 });
687
+ notify(result.content[0].text);
502
688
  return;
503
689
  }
504
- if (cmd === "keys" || cmd === "providers") {
505
- notify([
506
- "🔑 AI Model Providers",
507
- "",
508
- "Switch to CLI to manage keys:", " aiaiaichain keys",
509
- "",
510
- "Or use /models to see available models.",
511
- `\nProviders: ${modelRegistry.getProviderCount()} configured`,
512
- `Models: ${modelRegistry.modelCount} available`,
513
- ].join("\n"));
690
+ if (cmd === "diff" || cmd === "compared") {
691
+ const sessions = sessionStore.list();
692
+ if (sessions.length < 2) {
693
+ notify("Need at least 2 saved sessions to compare. Start a new session with /exit and restart.");
694
+ return;
695
+ }
696
+ const latest = sessions[0];
697
+ const prev = sessions[1];
698
+ if (!latest || !prev) {
699
+ notify("Could not load sessions for comparison.");
700
+ return;
701
+ }
702
+ const lines = [
703
+ `📊 Session Diff`,
704
+ `Current: ${latest.title} (${latest.messageCount} msgs)`,
705
+ `Previous: ${prev.title} (${prev.messageCount} msgs)`,
706
+ `Difference: ${latest.messageCount - prev.messageCount} messages`,
707
+ `Last active: ${new Date(latest.updatedAt).toLocaleString()}`,
708
+ ];
709
+ notify(lines.join("\n"));
514
710
  return;
515
711
  }
516
- if (cmd === "memory" && args.trim()) {
517
- const results = memoryStore.search(args.trim(), 8);
518
- if (results.length === 0) {
519
- notify(T.muted("No memories found for: " + args.trim()));
712
+ if (cmd === "copy" && args.trim()) {
713
+ const ok = copyToClipboard(args.trim());
714
+ notify(ok ? T.success(`✓ Copied to clipboard: ${args.slice(0, 30)}…`) : T.error("Clipboard not supported in this terminal. Try: " + clipboardSupported()));
715
+ return;
716
+ }
717
+ if (cmd === "theme") {
718
+ if (!args || args === "list") {
719
+ const current = getPresetColors();
720
+ notify(`🎨 Themes\n${listThemes()}\n\nApply: /theme <name>`);
721
+ }
722
+ else if (setThemePreset(args)) {
723
+ notify(T.success(`✓ Theme set to "${args}". Restart to apply.`));
724
+ }
725
+ else {
726
+ notify(T.error(`Unknown theme "${args}". Try: /theme list`));
727
+ }
728
+ return;
729
+ }
730
+ if (cmd === "simulate" || cmd === "sim") {
731
+ if (!args.trim()) {
732
+ notify(T.error("Usage: /simulate <base64-tx>"));
520
733
  return;
521
734
  }
522
- const lines = results.map(r => `[${new Date(r.ts).toLocaleDateString()} ${r.role}] ${r.content.slice(0, 120)}`);
523
- notify(`Memory search: "${args.trim()}"\n${lines.join("\n")}`);
735
+ const result = await simulateTxTool("", { transaction: args.trim() });
736
+ notify(result.content[0].text);
737
+ return;
738
+ }
739
+ if (cmd === "explain") {
740
+ if (!modelReg) {
741
+ notify(T.error("Model registry not available"));
742
+ return;
743
+ }
744
+ const lastAssistant = [...messages].reverse().find(m => m.role === "tool");
745
+ if (!lastAssistant) {
746
+ notify("No tool result to explain. Run a tool first.");
747
+ return;
748
+ }
749
+ push({ role: "user", content: `Explain this tool result in simple terms:\n${lastAssistant.content.slice(0, 2000)}` });
750
+ setDisabled(true);
751
+ setStreaming("");
752
+ try {
753
+ await runnerRef.current.run(`Explain this tool result simply:\n${lastAssistant.content.slice(0, 2000)}`);
754
+ }
755
+ catch (e) {
756
+ setDisabled(false);
757
+ notify(T.error(`Error: ${e.message}`));
758
+ }
759
+ return;
760
+ }
761
+ if (cmd === "webhook" || cmd === "webhooks") {
762
+ if (hasWebhooks()) {
763
+ notify("✅ Webhooks configured\n\nUsage: /webhook test\nConfig: WEBHOOK_DISCORD / WEBHOOK_TELEGRAM in ~/.aiaiai/.env");
764
+ }
765
+ else {
766
+ notify("No webhooks configured.\nAdd WEBHOOK_DISCORD=... or WEBHOOK_TELEGRAM=... to ~/.aiaiai/.env");
767
+ }
768
+ return;
769
+ }
770
+ if (cmd === "clipboard") {
771
+ notify(clipboardSupported());
524
772
  return;
525
773
  }
526
774
  const def = registry.getCommand(cmd);
527
775
  if (!def) {
528
- notify(T.error(`Unknown: /${cmd} try /help`));
776
+ const suggestions = suggestCommands(cmd);
777
+ const tip = suggestions.length > 0
778
+ ? `\nDid you mean: ${suggestions.map(s => `/${s}`).join(", ")}?`
779
+ : "";
780
+ notify(T.error(`Unknown: /${cmd} — try /help${tip}`));
529
781
  return;
530
782
  }
531
783
  try {
@@ -552,6 +804,7 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
552
804
  notify(T.error(`Error: ${e.message}`));
553
805
  }
554
806
  }, [registry, exit, push, notify, uiCtx, modelReg, costTracker]);
807
+ // ── Model selector ─────────────────────────────────────────────────────
555
808
  const handleModelSelect = useCallback((modelId) => {
556
809
  setShowModelSelector(false);
557
810
  try {
@@ -584,22 +837,22 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
584
837
  }, [modelReg]);
585
838
  const ctx = getContextBar(sessionRef.current);
586
839
  const statusLine = [costBadge, newsBadge, ...Object.values(statusBadges)].filter(Boolean).join(" ") || null;
587
- const changeColor = (pct) => pct > 0 ? AIAIAI_COLORS.success : pct < 0 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
588
840
  const SIDEBAR_W = 44;
589
841
  const SIDEBAR_MIN = 38;
590
- const actionIcon = (type) => type === "buy" ? "↗" : type === "burn" ? "🔥" : type === "deposit" ? "↓" : "→";
591
- if (showModelSelector) {
592
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusBar, { model: modelName, chain: chain, price: priceBadge, toolRunning: toolRunning, connected: true, statusLine: statusLine }), _jsx(ModelSelector, { models: modelList, currentModelId: process.env.DEFAULT_MODEL ?? "", onSelect: handleModelSelect, onCancel: handleModelCancel, initialQuery: modelSelectorInitialQuery })] }));
593
- }
594
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusBar, { model: modelName, chain: chain, price: priceBadge, toolRunning: toolRunning, connected: true, statusLine: statusLine }), _jsxs(Box, { flexDirection: "row", flexGrow: 1, overflow: "hidden", children: [_jsxs(Box, { flexDirection: "column", width: SIDEBAR_W, minWidth: SIDEBAR_MIN, flexShrink: 0, overflow: "hidden", children: [_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.accent, paddingX: 1, children: [_jsx(Text, { bold: true, color: AIAIAI_COLORS.accent, children: "\uD83E\uDD16 $AIAIAI" }), aiaiPrice ? (_jsxs(_Fragment, { children: [_jsxs(Text, { color: changeColor(aiaiPrice.change), children: [aiaiPrice.priceUsd ? `$${parseFloat(aiaiPrice.priceUsd).toFixed(8)}` : "N/A", " ", aiaiPrice.change > 0 ? "▲" : aiaiPrice.change < 0 ? "▼" : "─", Math.abs(aiaiPrice.change).toFixed(1), "%"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["MCap: ", aiaiPrice.mcap ? `$${(aiaiPrice.mcap / 1000).toFixed(1)}k` : "N/A"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Liq: $", (aiaiPrice.liq / 1000).toFixed(1), "k"] })] })) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Loading\u2026" })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDCBC Wallets" }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Cold: ", coldBalance.sol.toFixed(2), " SOL"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [" AIAIAI: ", coldBalance.aiaiai.toLocaleString(undefined, { maximumFractionDigits: 0 })] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Action: ", actionBalance.sol.toFixed(2), " SOL"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [" AIAIAI: ", actionBalance.aiaiai.toLocaleString(undefined, { maximumFractionDigits: 0 })] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Deposit: ", depositBalance.usdc.toFixed(2), " USDC"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [" SOL: ", depositBalance.sol.toFixed(2)] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\u26A1 Actions" }), recentActions.length > 0 ? recentActions.map((a, i) => {
842
+ const changeColor = (pct) => pct > 0 ? AIAIAI_COLORS.success : pct < 0 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
843
+ const actionIcon = (type) => type === "buy" ? "↗" : type === "burn" ? "🔥" : "→";
844
+ // Single StatusBar wrapper shared across both views to prevent duplication
845
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusBar, { model: modelName, chain: chain, price: priceBadge, toolRunning: toolRunning, connected: true, statusLine: statusLine, cpu: cpu, ram: ram, uptime: uptime, apiCalls: apiCalls }), showModelSelector ? (_jsx(ModelSelector, { models: modelList, currentModelId: process.env.DEFAULT_MODEL ?? "", onSelect: handleModelSelect, onCancel: handleModelCancel, initialQuery: modelSelectorInitialQuery })) : (_jsxs(Box, { flexDirection: "row", flexGrow: 1, overflow: "hidden", children: [_jsxs(Box, { flexDirection: "column", width: SIDEBAR_W, minWidth: SIDEBAR_MIN, flexShrink: 0, overflow: "hidden", children: [_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.accent, paddingX: 1, children: [_jsx(Text, { bold: true, color: AIAIAI_COLORS.accent, children: "\uD83E\uDD16 $AIAIAI" }), aiaiPrice ? (_jsxs(_Fragment, { children: [_jsxs(Text, { color: changeColor(aiaiPrice.change), children: [aiaiPrice.priceUsd ? `$${parseFloat(aiaiPrice.priceUsd).toFixed(8)}` : "N/A", " ", aiaiPrice.change > 0 ? "▲" : aiaiPrice.change < 0 ? "▼" : "─", Math.abs(aiaiPrice.change).toFixed(1), "%"] }), _jsx(Sparkline, { data: priceHistoryRef.current.getData(), width: 38, color: changeColor(aiaiPrice.change), label: "" }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["MCap: ", aiaiPrice.mcap ? `$${(aiaiPrice.mcap / 1000).toFixed(1)}k` : "N/A"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Liq: $", (aiaiPrice.liq / 1000).toFixed(1), "k"] })] })) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Loading\u2026" })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDCBC Wallets" }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Cold: ", coldBalance.sol.toFixed(2), " SOL"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [" AIAIAI: ", coldBalance.aiaiai.toLocaleString(undefined, { maximumFractionDigits: 0 })] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Action: ", actionBalance.sol.toFixed(2), " SOL"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [" AIAIAI: ", actionBalance.aiaiai.toLocaleString(undefined, { maximumFractionDigits: 0 })] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Deposit: ", depositBalance.usdc.toFixed(2), " USDC"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [" SOL: ", depositBalance.sol.toFixed(2)] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\u26A1 Actions" }), recentActions.length > 0 ? recentActions.map((a, i) => {
595
846
  const col = a.type === "buy" ? AIAIAI_COLORS.success : a.type === "burn" ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
596
847
  const time = new Date(a.timestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
597
848
  return _jsxs(Text, { color: col, children: [actionIcon(a.type), " ", time, " ", a.type, " ", a.amount.toLocaleString()] }, i);
598
- }) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Waiting for actions\u2026" })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDCB5 Fees (0.5%)" }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Buy: $", fees.buyFees.toFixed(4)] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Burn: $", fees.burnFees.toFixed(4)] }), _jsxs(Text, { color: AIAIAI_COLORS.accent, bold: true, children: ["Total: $", fees.total.toFixed(4)] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDCF0 News" }), sidebarNews.length > 0 ? sidebarNews.map((n, i) => {
849
+ }) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Waiting\u2026" })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDCB5 Fees (0.5%)" }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Buy: $", fees.buyFees.toFixed(4)] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Burn: $", fees.burnFees.toFixed(4)] }), _jsxs(Text, { color: AIAIAI_COLORS.accent, bold: true, children: ["Total: $", fees.total.toFixed(4)] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDC41\uFE0F Watch" }), watchList.length > 0 ? watchList.slice(0, 4).map((addr, i) => {
850
+ return _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [addr.slice(0, 8), "\u2026", addr.slice(-6)] }, i);
851
+ }) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "No tokens watched" })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDCF0 News" }), sidebarNews.length > 0 ? sidebarNews.slice(0, 4).map((n, i) => {
599
852
  const sentColor = n.sentiment > 0.3 ? AIAIAI_COLORS.success : n.sentiment < -0.3 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
600
853
  const title = n.title.length > 30 ? n.title.slice(0, 28) + "…" : n.title;
601
854
  return _jsx(Text, { color: sentColor, children: title }, i);
602
- }) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Loading\u2026" })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "Context" }), _jsxs(Text, { color: ctx.color, children: [ctx.bar, " ", ctx.pct, "%"] })] })] }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: _jsx(REPL, { messages: messages, streamingText: streaming, toolRunning: toolRunning, onSubmit: handleSubmit, disabled: disabled, onAbort: () => {
855
+ }) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Loading\u2026" })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "Context" }), _jsxs(Text, { color: ctx.color, children: [ctx.bar, " ", ctx.pct, "%"] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDE80 Recent" }), _jsx(Text, { color: AIAIAI_COLORS.muted, children: getLaunchSummary() })] })] }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: _jsx(REPL, { messages: messages, streamingText: streaming, toolRunning: toolRunning, onSubmit: handleSubmit, disabled: disabled, onAbort: () => {
603
856
  runnerRef.current?.abort();
604
857
  setDisabled(false);
605
858
  setToolRunning(null);
@@ -609,6 +862,6 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
609
862
  return "";
610
863
  });
611
864
  push({ role: "system", content: T.dim("— stream interrupted —") });
612
- } }) })] })] }));
865
+ } }) })] }))] }));
613
866
  }
614
867
  //# sourceMappingURL=App.js.map