@aiaiaichain/agent 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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";
@@ -24,8 +24,12 @@ import { contextStore } from "../session/ContextStore.js";
24
24
  import { goalManager } from "../session/GoalManager.js";
25
25
  import { memoryStore } from "../session/MemoryStore.js";
26
26
  import { agentScheduler } from "../scheduler/AgentScheduler.js";
27
+ import { sessionStore } from "../session/SessionStore.js";
27
28
  import { agentWallet, ACTION_WALLET, DEPOSIT_WALLET, SIGNER } from "../wallet/AgentWallet.js";
28
29
  import { actionFeed } from "../wallet/ActionFeed.js";
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";
29
33
  function getContextBar(session) {
30
34
  if (!session)
31
35
  return { pct: 0, bar: "░".repeat(20), color: AIAIAI_COLORS.dim };
@@ -90,9 +94,14 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
90
94
  const [actionBalance, setActionBalance] = useState({ sol: 0, aiaiai: 0, usdc: 0 });
91
95
  const [depositBalance, setDepositBalance] = useState({ sol: 0, aiaiai: 0, usdc: 0 });
92
96
  const [recentActions, setRecentActions] = useState([]);
93
- const [fees, setFees] = useState({ buyFees: 0, burnFees: 0, sellFees: 0, total: 0 });
97
+ const [fees, setFees] = useState({ buyFees: 0, burnFees: 0, total: 0 });
94
98
  const [showModelSelector, setShowModelSelector] = useState(false);
95
99
  const [modelSelectorInitialQuery, setModelSelectorInitialQuery] = useState("");
100
+ const [cpu, setCpu] = useState(0);
101
+ const [ram, setRam] = useState(0);
102
+ const [uptime, setUptime] = useState("0m");
103
+ const [apiCalls, setApiCalls] = useState(0);
104
+ const [watchList, setWatchList] = useState([]);
96
105
  const runnerRef = useRef(null);
97
106
  const sessionRef = useRef(null);
98
107
  const sessionCtxRef = useRef(null);
@@ -118,6 +127,7 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
118
127
  },
119
128
  theme,
120
129
  };
130
+ // ── Register ALL tools ──────────────────────────────────────────────────
121
131
  function registerBuiltinTools() {
122
132
  if (!modelReg)
123
133
  return;
@@ -128,11 +138,11 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
128
138
  if (costTracker) {
129
139
  registry.addTool({ name: "cost_report", label: "Cost Report", description: "Show session and lifetime token usage.", parameters: costTracker.costReportParams, execute: () => costTracker.costReportTool() });
130
140
  }
131
- 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() });
132
- 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) });
133
- registry.addTool({ name: "get_news", label: "Get News", description: "Crypto news headlines with sentiment scoring.", parameters: getNewsParams, execute: (id, p) => getNewsTool(id, p) });
134
- 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) });
135
- registry.addTool({ name: "analyze_ta", label: "Technical Analysis", description: "Run RSI, MACD, Bollinger, ATR on a price array.", parameters: analyzeTAParams, execute: async (_id, p) => {
141
+ registry.addTool({ name: "get_aiaiai_price", label: "AIAIAI Price", description: "Get $AIAIAI price, liquidity, MCap, volume.", parameters: priceFeed.getAiaiaiPriceParams, execute: () => priceFeed.getAiaiaiPriceTool() });
142
+ 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) });
143
+ registry.addTool({ name: "get_news", label: "Get News", description: "Crypto news with sentiment.", parameters: getNewsParams, execute: (id, p) => getNewsTool(id, p) });
144
+ registry.addTool({ name: "get_candles", label: "Get Candles", description: "OHLCV from Binance + TA.", parameters: getCandlesParams, execute: (id, p) => getCandlesTool(id, p) });
145
+ registry.addTool({ name: "analyze_ta", label: "Technical Analysis", description: "RSI, MACD, Bollinger, ATR.", parameters: analyzeTAParams, execute: async (_id, p) => {
136
146
  const closes = p.prices;
137
147
  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 }));
138
148
  const results = fullAnalysis(candles);
@@ -144,33 +154,37 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
144
154
  }).join("\n");
145
155
  return { content: [{ type: "text", text: `Technical Analysis:\n${text}` }], details: { results, overall_signal: summary?.signal } };
146
156
  } });
147
- registry.addTool({ name: "get_fear_greed", label: "Fear & Greed", description: "Crypto Fear & Greed Index with 7-day history.", parameters: fearGreedParams, execute: () => getFearGreedTool() });
148
- registry.addTool({ name: "get_funding_rates", label: "Funding Rates", description: "Binance perpetual funding rates.", parameters: fundingRatesParams, execute: (id, p) => getFundingRatesTool(id, p) });
149
- registry.addTool({ name: "get_btc_mempool", label: "BTC Mempool", description: "Bitcoin mempool stats and fee rates.", parameters: btcMempoolParams, execute: () => getBtcMempoolTool() });
150
- 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) });
151
- registry.addTool({ name: "get_solana_stats", label: "Solana Stats", description: "Solana network epoch, slot, and SOL price.", parameters: solanaStatsParams, execute: () => getSolanaStatsTool() });
157
+ registry.addTool({ name: "get_fear_greed", label: "Fear & Greed", description: "Crypto Fear & Greed Index.", parameters: fearGreedParams, execute: () => getFearGreedTool() });
158
+ registry.addTool({ name: "get_funding_rates", label: "Funding Rates", description: "Binance funding rates.", parameters: fundingRatesParams, execute: (id, p) => getFundingRatesTool(id, p) });
159
+ registry.addTool({ name: "get_btc_mempool", label: "BTC Mempool", description: "BTC mempool stats.", parameters: btcMempoolParams, execute: () => getBtcMempoolTool() });
160
+ registry.addTool({ name: "get_defi_tvl", label: "DeFi TVL", description: "DeFiLlama TVL.", parameters: defiTvlParams, execute: (id, p) => getDefiTvlTool(id, p) });
161
+ registry.addTool({ name: "get_solana_stats", label: "Solana Stats", description: "Solana epoch, slot, SOL price.", parameters: solanaStatsParams, execute: () => getSolanaStatsTool() });
162
+ registry.addTool({ name: "get_agent_balance", label: "Agent Balance", description: "Cold + action wallet balances.", parameters: agentWallet.getAgentBalanceParams, execute: () => agentWallet.getAgentBalanceTool() });
163
+ registry.addTool({ name: "get_deposit_balance", label: "Deposit Balance", description: "Deposit wallet + instructions.", parameters: agentWallet.getDepositBalanceParams, execute: () => agentWallet.getDepositBalanceTool() });
164
+ registry.addTool({ name: "get_actions", label: "Agent Actions", description: "Recent buy/burn actions.", parameters: actionFeed.getActionsParams, execute: (id, p) => actionFeed.getActionsTool(id, p) });
165
+ registry.addTool({ name: "get_fees", label: "Agent Fees", description: "Accumulated fees.", parameters: actionFeed.getFeesParams, execute: () => actionFeed.getFeesTool() });
152
166
  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) });
153
167
  registry.addTool({ name: "list_tasks", label: "List Tasks", description: "List saved task contexts.", parameters: Type.Object({}), execute: () => contextStore.listTasksTool() });
154
- 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) });
168
+ registry.addTool({ name: "set_goal", label: "Set Goal", description: "Set a persistent goal.", parameters: goalManager.setGoalParams, execute: (id, p) => goalManager.setGoalTool(id, p) });
155
169
  registry.addTool({ name: "complete_goal", label: "Complete Goal", description: "Mark a goal complete.", parameters: goalManager.completeGoalParams, execute: (id, p) => goalManager.completeGoalTool(id, p) });
156
170
  registry.addTool({ name: "list_goals", label: "List Goals", description: "List goals.", parameters: goalManager.listGoalsParams, execute: (id, p) => goalManager.listGoalsTool(id, p) });
157
171
  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) });
158
- registry.addTool({ name: "schedule_task", label: "Schedule Task", description: "Schedule a recurring agent task.", parameters: agentScheduler.addTaskParams, execute: (id, p) => agentScheduler.addTaskTool(id, p) });
172
+ registry.addTool({ name: "schedule_task", label: "Schedule Task", description: "Schedule a recurring task.", parameters: agentScheduler.addTaskParams, execute: (id, p) => agentScheduler.addTaskTool(id, p) });
159
173
  registry.addTool({ name: "list_schedule", label: "List Schedule", description: "List scheduled tasks.", parameters: agentScheduler.listTasksParams, execute: (id, p) => agentScheduler.listTasksTool(id, p) });
160
174
  registry.addTool({ name: "remove_schedule", label: "Remove Schedule", description: "Remove a scheduled task.", parameters: agentScheduler.removeTaskParams, execute: (id, p) => agentScheduler.removeTaskTool(id, p) });
161
- // Wallet tools
162
- registry.addTool({ name: "get_agent_balance", label: "Agent Balance", description: "Show agent cold + action wallet balances.", parameters: agentWallet.getAgentBalanceParams, execute: () => agentWallet.getAgentBalanceTool() });
163
- registry.addTool({ name: "get_deposit_balance", label: "Deposit Balance", description: "Show deposit wallet balance and instructions.", parameters: agentWallet.getDepositBalanceParams, execute: () => agentWallet.getDepositBalanceTool() });
164
- 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) });
165
- registry.addTool({ name: "get_fees", label: "Agent Fees", description: "Show accumulated fees from agent actions.", parameters: actionFeed.getFeesParams, execute: () => actionFeed.getFeesTool() });
175
+ // GMGN tools
176
+ 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) });
177
+ registry.addTool({ name: "gmgn_market", label: "GMGN Market Data", description: "Market data: trending, trenches, kline, signals.", parameters: gmgnMarketToolParams, execute: (id, p) => gmgnMarketTool(id, p) });
178
+ // Cross tools
179
+ registry.addTool({ name: "watch_token", label: "Watch Token", description: "Add token to watch list.", parameters: watchTokenParams, execute: (id, p) => watchTokenTool(id, p) });
180
+ registry.addTool({ name: "unwatch_token", label: "Unwatch Token", description: "Remove token from watch list.", parameters: removeWatchParams, execute: (id, p) => removeWatchTool(id, p) });
181
+ registry.addTool({ name: "watch_list", label: "Watch List", description: "Show watched tokens.", parameters: listWatchParams, execute: () => listWatchTool() });
182
+ registry.addTool({ name: "add_alert", label: "Price Alert", description: "Set a price alert.", parameters: addAlertParams, execute: (id, p) => addAlertTool(id, p) });
183
+ registry.addTool({ name: "check_alerts", label: "Check Alerts", description: "List active alerts.", parameters: checkAlertsParams, execute: () => checkAlertsTool() });
184
+ registry.addTool({ name: "compare_tokens", label: "Compare Tokens", description: "Side-by-side token comparison.", parameters: compareTokensParams, execute: (id, p) => compareTokensTool(id, p) });
185
+ registry.addTool({ name: "portfolio", label: "Portfolio", description: "Wallet portfolio analysis.", parameters: portfolioParams, execute: (id, p) => portfolioTool(id, p) });
166
186
  }
167
- useEffect(() => { onNotifyReady?.(notify); onStatusReady?.(setStatus); }, []);
168
- useEffect(() => {
169
- onModelSelectorReady?.((initialQuery) => {
170
- setModelSelectorInitialQuery(initialQuery ?? "");
171
- setShowModelSelector(true);
172
- });
173
- }, [onModelSelectorReady]);
187
+ // ── Main effect: setup + intervals ──────────────────────────────────────
174
188
  useEffect(() => {
175
189
  registerBuiltinTools();
176
190
  // Initial price fetch
@@ -196,8 +210,11 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
196
210
  setSidebarNews(report.items.slice(0, 6).map(i => ({ title: i.title, sentiment: i.sentiment ?? 0, source: i.source })));
197
211
  setNewsBadge(newsFeed.statusBadge());
198
212
  }).catch(() => { });
213
+ // Load watch list
214
+ setWatchList(_getWatchList());
215
+ // Scheduler
199
216
  agentScheduler.start((task) => {
200
- push({ role: "system", content: T.muted(`⏰ Scheduled task: "${task.name}"`) });
217
+ push({ role: "system", content: T.muted(`⏰ Scheduled: "${task.name}"`) });
201
218
  if (!disabled && runnerRef.current) {
202
219
  setDisabled(true);
203
220
  setStreaming("");
@@ -212,6 +229,14 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
212
229
  if (p.priceUsd)
213
230
  setPriceBadge(`$${parseFloat(p.priceUsd).toFixed(6)}`);
214
231
  actionFeed.setPrice(parseFloat(p.priceUsd || "0.0004"));
232
+ systemMonitor.trackApiCall();
233
+ // Check alerts
234
+ if (p.priceUsd) {
235
+ const triggered = _checkAlerts(parseFloat(p.priceUsd), "AIAIAI");
236
+ for (const alert of triggered) {
237
+ notify(`🔔 Alert triggered: ${alert.type} $${alert.price} — $AIAIAI is now $${p.priceUsd}`);
238
+ }
239
+ }
215
240
  }).catch(() => { });
216
241
  if (costTracker)
217
242
  setCostBadge(costTracker.statusLine());
@@ -235,7 +260,21 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
235
260
  setNewsBadge(newsFeed.statusBadge());
236
261
  }).catch(() => { });
237
262
  }, 300_000);
238
- const saveInterval = setInterval(() => { costTracker?.saveLifetime(); }, 60_000);
263
+ // System monitor interval (10s)
264
+ const sysInterval = setInterval(() => {
265
+ const stats = systemMonitor.getStats();
266
+ setCpu(stats.cpuPercent);
267
+ setRam(stats.ramPercent);
268
+ setUptime(stats.uptime);
269
+ setApiCalls(systemMonitor.resetApiCounter());
270
+ }, 10_000);
271
+ // Session auto-save (30s)
272
+ sessionStore.startAutoSave(() => messages);
273
+ sessionStore.setCurrentId(sessionId);
274
+ const saveInterval = setInterval(() => {
275
+ if (messages.length > 0)
276
+ sessionStore.save(messages);
277
+ }, 30_000);
239
278
  const session = new SessionManager();
240
279
  session.setSystemPrompt(registry.getSystemPrompt() || systemPrompt || "You are AIAIAI Chain Agent.");
241
280
  sessionRef.current = session;
@@ -299,9 +338,13 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
299
338
  if (sessionCtxRef.current)
300
339
  registry.fireHook("session_end", sessionCtxRef.current).catch(() => { });
301
340
  agentScheduler.stop();
341
+ sessionStore.stopAutoSave();
342
+ if (messages.length > 0)
343
+ sessionStore.save(messages);
302
344
  clearInterval(priceInterval);
303
345
  clearInterval(walletInterval);
304
346
  clearInterval(newsInterval);
347
+ clearInterval(sysInterval);
305
348
  clearInterval(saveInterval);
306
349
  costTracker?.saveLifetime();
307
350
  };
@@ -309,10 +352,13 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
309
352
  useInput((_input, key) => {
310
353
  if (key.ctrl && _input === "c") {
311
354
  push({ role: "system", content: T.muted("Goodbye 🤖") });
355
+ if (messages.length > 0)
356
+ sessionStore.save(messages);
312
357
  costTracker?.saveLifetime();
313
358
  setTimeout(exit, 200);
314
359
  }
315
360
  });
361
+ // ── Submit handler ─────────────────────────────────────────────────────
316
362
  const handleSubmit = useCallback(async (raw) => {
317
363
  const input = raw.trim();
318
364
  if (!input)
@@ -321,16 +367,157 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
321
367
  const [cmd, ...rest] = input.slice(1).split(" ");
322
368
  const args = rest.join(" ");
323
369
  if (cmd === "exit" || cmd === "quit") {
324
- push({ role: "system", content: T.muted("Goodbye 🤖") });
370
+ if (messages.length > 0)
371
+ sessionStore.save(messages);
325
372
  costTracker?.saveLifetime();
373
+ push({ role: "system", content: T.muted("Goodbye 🤖") });
326
374
  setTimeout(exit, 200);
327
375
  return;
328
376
  }
329
377
  if (cmd === "help") {
330
- const lines = registry.listCommands().map(([n, d]) => T.accent(`/${n}`.padEnd(16)) + " " + d.description);
331
- notify("Commands:\n/wallet /deposit /burn /actions /fees /price /news /models /cost /goals /schedule /model /clear /exit\n" + (lines.length ? "\n" + lines.join("\n") : ""));
378
+ 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\n/sessions /resume /memory /clear /exit\n\nTab to auto-complete, ↑↓ for history, Shift+Enter for multi-line");
379
+ return;
380
+ }
381
+ // ── GMGN direct commands ──────────────────────────────────────────
382
+ if (cmd === "token") {
383
+ // /token info --chain sol --address <addr>
384
+ const parts = args.split(" ");
385
+ const subcmd = parts[0];
386
+ if (!subcmd || subcmd === "help") {
387
+ 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>");
388
+ return;
389
+ }
390
+ const chain = parts[parts.indexOf("--chain") + 1] || "sol";
391
+ const address = parts[parts.indexOf("--address") + 1];
392
+ if (!address && subcmd !== "help") {
393
+ notify(T.error("--address is required"));
394
+ return;
395
+ }
396
+ const result = await gmgnTool("", { subcommand: subcmd, chain, address });
397
+ notify(result.content[0].text);
398
+ return;
399
+ }
400
+ if (cmd === "track") {
401
+ // /track kol --chain sol
402
+ const parts = args.split(" ");
403
+ const subcmd = parts[0];
404
+ if (!subcmd || subcmd === "help") {
405
+ notify("Track commands:\n/track kol --chain <sol|bsc|base|eth>\n/track smartmoney --chain <chain>\n/track follow-wallet --chain <chain>");
406
+ return;
407
+ }
408
+ const chain = parts[parts.indexOf("--chain") + 1] || "sol";
409
+ const result = await gmgnTool("", { subcommand: `track ${subcmd}`, chain, limit: 10 });
410
+ notify(result.content[0].text);
411
+ return;
412
+ }
413
+ if (cmd === "market") {
414
+ // /market kline --chain sol --address <addr> --resolution 1h
415
+ const parts = args.split(" ");
416
+ const subcmd = parts[0];
417
+ if (!subcmd || subcmd === "help") {
418
+ 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>");
419
+ return;
420
+ }
421
+ const chain = parts[parts.indexOf("--chain") + 1] || "sol";
422
+ const address = parts[parts.indexOf("--address") + 1];
423
+ const resolution = parts[parts.indexOf("--resolution") + 1];
424
+ const interval = parts[parts.indexOf("--interval") + 1];
425
+ const trenchesType = parts[parts.indexOf("--type") + 1];
426
+ const toolParams = { subcommand: `market ${subcmd}`, chain };
427
+ if (address)
428
+ toolParams.address = address;
429
+ if (resolution)
430
+ toolParams.resolution = resolution;
431
+ if (interval)
432
+ toolParams.interval = interval;
433
+ if (trenchesType)
434
+ toolParams.type = trenchesType;
435
+ const result = await gmgnTool("", toolParams);
436
+ notify(result.content[0].text);
437
+ return;
438
+ }
439
+ // ── Cross tools ────────────────────────────────────────────────────
440
+ if (cmd === "watch") {
441
+ const result = await watchTokenTool("", { address: args || "AVPJS61gZmWKtaEpb7qYPKo8Fk2xQUsayYQxPiPMpump" });
442
+ setWatchList(_getWatchList());
443
+ notify(result.content[0].text);
444
+ return;
445
+ }
446
+ if (cmd === "unwatch") {
447
+ const result = await removeWatchTool("", { address: args });
448
+ setWatchList(_getWatchList());
449
+ notify(result.content[0].text);
450
+ return;
451
+ }
452
+ if (cmd === "watchlist") {
453
+ const result = await listWatchTool();
454
+ notify(result.content[0].text);
455
+ return;
456
+ }
457
+ if (cmd === "alerts") {
458
+ const result = await checkAlertsTool();
459
+ notify(result.content[0].text);
460
+ return;
461
+ }
462
+ if (cmd === "alert") {
463
+ // /alert above 0.001 AIAIAI
464
+ const parts = args.split(" ");
465
+ const type = parts[0];
466
+ const price = parseFloat(parts[1]);
467
+ if (!type || isNaN(price)) {
468
+ notify(T.error("Usage: /alert above|below <price>"));
469
+ return;
470
+ }
471
+ const result = await addAlertTool("", { token: "AIAIAI", type, price });
472
+ notify(result.content[0].text);
473
+ return;
474
+ }
475
+ if (cmd === "compare") {
476
+ // /compare <addr1> <addr2>
477
+ const parts = args.split(" ");
478
+ if (parts.length < 2) {
479
+ notify(T.error("Usage: /compare <address1> <address2>"));
480
+ return;
481
+ }
482
+ const result = await compareTokensTool("", { address1: parts[0], address2: parts[1] });
483
+ notify(result.content[0].text);
484
+ return;
485
+ }
486
+ if (cmd === "portfolio") {
487
+ const addr = args || "A11iZoqEt6hU7HyggqC67ee4AtYmaJjwKCvJLerJRV2J";
488
+ const result = await portfolioTool("", { address: addr });
489
+ notify(result.content[0].text);
490
+ return;
491
+ }
492
+ // ── Session management ─────────────────────────────────────────────
493
+ if (cmd === "sessions") {
494
+ const sessions = sessionStore.list();
495
+ if (sessions.length === 0) {
496
+ notify("No saved sessions.");
497
+ return;
498
+ }
499
+ const lines = sessions.slice(0, 10).map((s, i) => ` ${i + 1}. ${s.title} (${s.messageCount} msgs, ${new Date(s.updatedAt).toLocaleDateString()})`);
500
+ notify(`Recent Sessions:\n${lines.join("\n")}\n\nUse /resume <number> to resume.`);
501
+ return;
502
+ }
503
+ if (cmd === "resume") {
504
+ const sessions = sessionStore.list();
505
+ const idx = parseInt(args) - 1;
506
+ if (isNaN(idx) || idx < 0 || idx >= sessions.length) {
507
+ notify(T.error("Invalid session number."));
508
+ return;
509
+ }
510
+ const session = sessionStore.load(sessions[idx].id);
511
+ if (!session) {
512
+ notify(T.error("Session not found."));
513
+ return;
514
+ }
515
+ setMessages(session.messages);
516
+ sessionStore.setCurrentId(session.id);
517
+ notify(`Resumed: ${session.title} (${session.messages.length} messages)`);
332
518
  return;
333
519
  }
520
+ // ── Standard commands ───────────────────────────────────────────────
334
521
  if (cmd === "price") {
335
522
  const result = await priceFeed.getAiaiaiPriceTool();
336
523
  notify(result.content[0].text);
@@ -379,20 +566,7 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
379
566
  return;
380
567
  }
381
568
  if (cmd === "burn") {
382
- notify([
383
- "🔥 Burn $AIAIAI",
384
- "",
385
- `Deposit wallet: ${DEPOSIT_WALLET}`,
386
- `Send SOL or USDC here to fuel agent burns.`,
387
- "",
388
- `The agent detects deposits and automatically`,
389
- `sends funds to the action wallet to burn tokens.`,
390
- "",
391
- `Action wallet: ${ACTION_WALLET}`,
392
- `Signer: ${SIGNER}`,
393
- "",
394
- `Burn destination: tokens are removed from circulation.`,
395
- ].join("\n"));
569
+ notify(["🔥 Burn $AIAIAI", "", `Deposit: ${DEPOSIT_WALLET}`, "Send SOL or USDC here.", "", `Action: ${ACTION_WALLET}`, `Signer: ${SIGNER}`].join("\n"));
396
570
  return;
397
571
  }
398
572
  if (cmd === "actions" || cmd === "activity") {
@@ -405,6 +579,10 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
405
579
  notify(result.content[0].text);
406
580
  return;
407
581
  }
582
+ if (cmd === "keys" || cmd === "providers") {
583
+ notify("Switch to CLI for key management:\n aiaiai keys");
584
+ return;
585
+ }
408
586
  if (cmd === "goals" || cmd === "goal") {
409
587
  const subCmd = args.trim().split(" ")[0];
410
588
  if (subCmd === "add") {
@@ -436,34 +614,41 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
436
614
  notify(result.content[0].text);
437
615
  return;
438
616
  }
439
- if (cmd === "approve" || cmd === "y") {
440
- const pending = runnerRef.current?._pendingApproval;
441
- if (pending) {
442
- pending(true);
443
- push({ role: "system", content: T.success("✅ Tool approved.") });
444
- }
445
- else
446
- notify(T.error("No pending approval."));
447
- return;
448
- }
449
- if (cmd === "deny" || cmd === "n") {
450
- const pending = runnerRef.current?._pendingApproval;
451
- if (pending) {
452
- pending(false);
453
- push({ role: "system", content: T.error("❌ Tool denied.") });
454
- }
455
- else
456
- notify(T.error("No pending approval."));
617
+ if (cmd === "update" || cmd === "upgrade") {
618
+ notify("Switch to CLI:\n aiaiai update");
457
619
  return;
458
620
  }
459
621
  if (cmd === "memory" && args.trim()) {
460
622
  const results = memoryStore.search(args.trim(), 8);
461
623
  if (results.length === 0) {
462
- notify(T.muted("No memories found for: " + args.trim()));
624
+ notify(T.muted("No memories found."));
463
625
  return;
464
626
  }
465
627
  const lines = results.map(r => `[${new Date(r.ts).toLocaleDateString()} ${r.role}] ${r.content.slice(0, 120)}`);
466
- notify(`Memory search: "${args.trim()}"\n${lines.join("\n")}`);
628
+ notify(`Memory: "${args.trim()}"\n${lines.join("\n")}`);
629
+ return;
630
+ }
631
+ if (cmd === "gmgn" || cmd === "gmgnhelp") {
632
+ const gmgnArgs = rest.join(" ");
633
+ if (!gmgnArgs || gmgnArgs === "help") {
634
+ notify(gmgnHelp());
635
+ return;
636
+ }
637
+ if (gmgnArgs === "status") {
638
+ notify(gmgnStatus());
639
+ return;
640
+ }
641
+ if (gmgnArgs.startsWith("setup")) {
642
+ notify("Run in CLI: aiaiai gmgn setup");
643
+ return;
644
+ }
645
+ const result = await gmgnTool("", { subcommand: gmgnArgs.split(" ")[0], chain: "sol" });
646
+ notify(result.content[0].text);
647
+ return;
648
+ }
649
+ if (cmd === "gmgnmarket" || cmd === "market") {
650
+ const result = await gmgnMarketTool("", { query: args || "trending", chain: "sol" });
651
+ notify(result.content[0].text);
467
652
  return;
468
653
  }
469
654
  const def = registry.getCommand(cmd);
@@ -495,6 +680,7 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
495
680
  notify(T.error(`Error: ${e.message}`));
496
681
  }
497
682
  }, [registry, exit, push, notify, uiCtx, modelReg, costTracker]);
683
+ // ── Model selector ─────────────────────────────────────────────────────
498
684
  const handleModelSelect = useCallback((modelId) => {
499
685
  setShowModelSelector(false);
500
686
  try {
@@ -527,17 +713,20 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
527
713
  }, [modelReg]);
528
714
  const ctx = getContextBar(sessionRef.current);
529
715
  const statusLine = [costBadge, newsBadge, ...Object.values(statusBadges)].filter(Boolean).join(" ") || null;
530
- const changeColor = (pct) => pct > 0 ? AIAIAI_COLORS.success : pct < 0 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
531
716
  const SIDEBAR_W = 44;
532
- const actionIcon = (type) => type === "buy" ? "↗" : type === "burn" ? "🔥" : type === "deposit" ? "↓" : "→";
717
+ const SIDEBAR_MIN = 38;
718
+ const changeColor = (pct) => pct > 0 ? AIAIAI_COLORS.success : pct < 0 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
719
+ const actionIcon = (type) => type === "buy" ? "↗" : type === "burn" ? "🔥" : "→";
533
720
  if (showModelSelector) {
534
- 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 })] }));
721
+ 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 }), _jsx(ModelSelector, { models: modelList, currentModelId: process.env.DEFAULT_MODEL ?? "", onSelect: handleModelSelect, onCancel: handleModelCancel, initialQuery: modelSelectorInitialQuery })] }));
535
722
  }
536
- 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, flexShrink: 0, 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) => {
723
+ 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 }), _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) => {
537
724
  const col = a.type === "buy" ? AIAIAI_COLORS.success : a.type === "burn" ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
538
725
  const time = new Date(a.timestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
539
726
  return _jsxs(Text, { color: col, children: [actionIcon(a.type), " ", time, " ", a.type, " ", a.amount.toLocaleString()] }, i);
540
- }) : _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" }), _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.success, 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) => {
727
+ }) : _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) => {
728
+ return _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [addr.slice(0, 8), "\u2026", addr.slice(-6)] }, i);
729
+ }) : _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) => {
541
730
  const sentColor = n.sentiment > 0.3 ? AIAIAI_COLORS.success : n.sentiment < -0.3 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
542
731
  const title = n.title.length > 30 ? n.title.slice(0, 28) + "…" : n.title;
543
732
  return _jsx(Text, { color: sentColor, children: title }, i);
@@ -1,5 +1,6 @@
1
1
  /**
2
- * REPL — scrolling message history + bottom input box.
2
+ * REPL — scrolling message history + feature-rich input box.
3
+ * Supports: multi-line (Shift+Enter), command history (↑/↓), tab completion.
3
4
  */
4
5
  import React from "react";
5
6
  export interface ChatMessage {