@aiaiaichain/agent 0.1.3 → 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";
@@ -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,12 @@ 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";
31
33
  function getContextBar(session) {
32
34
  if (!session)
33
35
  return { pct: 0, bar: "░".repeat(20), color: AIAIAI_COLORS.dim };
@@ -95,6 +97,11 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
95
97
  const [fees, setFees] = useState({ buyFees: 0, burnFees: 0, total: 0 });
96
98
  const [showModelSelector, setShowModelSelector] = useState(false);
97
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([]);
98
105
  const runnerRef = useRef(null);
99
106
  const sessionRef = useRef(null);
100
107
  const sessionCtxRef = useRef(null);
@@ -120,6 +127,7 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
120
127
  },
121
128
  theme,
122
129
  };
130
+ // ── Register ALL tools ──────────────────────────────────────────────────
123
131
  function registerBuiltinTools() {
124
132
  if (!modelReg)
125
133
  return;
@@ -130,11 +138,11 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
130
138
  if (costTracker) {
131
139
  registry.addTool({ name: "cost_report", label: "Cost Report", description: "Show session and lifetime token usage.", parameters: costTracker.costReportParams, execute: () => costTracker.costReportTool() });
132
140
  }
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) => {
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) => {
138
146
  const closes = p.prices;
139
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 }));
140
148
  const results = fullAnalysis(candles);
@@ -146,36 +154,37 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
146
154
  }).join("\n");
147
155
  return { content: [{ type: "text", text: `Technical Analysis:\n${text}` }], details: { results, overall_signal: summary?.signal } };
148
156
  } });
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() });
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() });
154
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) });
155
167
  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) });
168
+ registry.addTool({ name: "set_goal", label: "Set Goal", description: "Set a persistent goal.", parameters: goalManager.setGoalParams, execute: (id, p) => goalManager.setGoalTool(id, p) });
157
169
  registry.addTool({ name: "complete_goal", label: "Complete Goal", description: "Mark a goal complete.", parameters: goalManager.completeGoalParams, execute: (id, p) => goalManager.completeGoalTool(id, p) });
158
170
  registry.addTool({ name: "list_goals", label: "List Goals", description: "List goals.", parameters: goalManager.listGoalsParams, execute: (id, p) => goalManager.listGoalsTool(id, p) });
159
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) });
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) });
172
+ registry.addTool({ name: "schedule_task", label: "Schedule Task", description: "Schedule a recurring task.", parameters: agentScheduler.addTaskParams, execute: (id, p) => agentScheduler.addTaskTool(id, p) });
161
173
  registry.addTool({ name: "list_schedule", label: "List Schedule", description: "List scheduled tasks.", parameters: agentScheduler.listTasksParams, execute: (id, p) => agentScheduler.listTasksTool(id, p) });
162
174
  registry.addTool({ name: "remove_schedule", label: "Remove Schedule", description: "Remove a scheduled task.", parameters: agentScheduler.removeTaskParams, execute: (id, p) => agentScheduler.removeTaskTool(id, p) });
163
175
  // 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() });
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) });
171
186
  }
172
- useEffect(() => { onNotifyReady?.(notify); onStatusReady?.(setStatus); }, []);
173
- useEffect(() => {
174
- onModelSelectorReady?.((initialQuery) => {
175
- setModelSelectorInitialQuery(initialQuery ?? "");
176
- setShowModelSelector(true);
177
- });
178
- }, [onModelSelectorReady]);
187
+ // ── Main effect: setup + intervals ──────────────────────────────────────
179
188
  useEffect(() => {
180
189
  registerBuiltinTools();
181
190
  // Initial price fetch
@@ -201,8 +210,11 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
201
210
  setSidebarNews(report.items.slice(0, 6).map(i => ({ title: i.title, sentiment: i.sentiment ?? 0, source: i.source })));
202
211
  setNewsBadge(newsFeed.statusBadge());
203
212
  }).catch(() => { });
213
+ // Load watch list
214
+ setWatchList(_getWatchList());
215
+ // Scheduler
204
216
  agentScheduler.start((task) => {
205
- push({ role: "system", content: T.muted(`⏰ Scheduled task: "${task.name}"`) });
217
+ push({ role: "system", content: T.muted(`⏰ Scheduled: "${task.name}"`) });
206
218
  if (!disabled && runnerRef.current) {
207
219
  setDisabled(true);
208
220
  setStreaming("");
@@ -217,6 +229,14 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
217
229
  if (p.priceUsd)
218
230
  setPriceBadge(`$${parseFloat(p.priceUsd).toFixed(6)}`);
219
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
+ }
220
240
  }).catch(() => { });
221
241
  if (costTracker)
222
242
  setCostBadge(costTracker.statusLine());
@@ -240,7 +260,21 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
240
260
  setNewsBadge(newsFeed.statusBadge());
241
261
  }).catch(() => { });
242
262
  }, 300_000);
243
- 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);
244
278
  const session = new SessionManager();
245
279
  session.setSystemPrompt(registry.getSystemPrompt() || systemPrompt || "You are AIAIAI Chain Agent.");
246
280
  sessionRef.current = session;
@@ -304,9 +338,13 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
304
338
  if (sessionCtxRef.current)
305
339
  registry.fireHook("session_end", sessionCtxRef.current).catch(() => { });
306
340
  agentScheduler.stop();
341
+ sessionStore.stopAutoSave();
342
+ if (messages.length > 0)
343
+ sessionStore.save(messages);
307
344
  clearInterval(priceInterval);
308
345
  clearInterval(walletInterval);
309
346
  clearInterval(newsInterval);
347
+ clearInterval(sysInterval);
310
348
  clearInterval(saveInterval);
311
349
  costTracker?.saveLifetime();
312
350
  };
@@ -314,10 +352,13 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
314
352
  useInput((_input, key) => {
315
353
  if (key.ctrl && _input === "c") {
316
354
  push({ role: "system", content: T.muted("Goodbye 🤖") });
355
+ if (messages.length > 0)
356
+ sessionStore.save(messages);
317
357
  costTracker?.saveLifetime();
318
358
  setTimeout(exit, 200);
319
359
  }
320
360
  });
361
+ // ── Submit handler ─────────────────────────────────────────────────────
321
362
  const handleSubmit = useCallback(async (raw) => {
322
363
  const input = raw.trim();
323
364
  if (!input)
@@ -326,16 +367,157 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
326
367
  const [cmd, ...rest] = input.slice(1).split(" ");
327
368
  const args = rest.join(" ");
328
369
  if (cmd === "exit" || cmd === "quit") {
329
- push({ role: "system", content: T.muted("Goodbye 🤖") });
370
+ if (messages.length > 0)
371
+ sessionStore.save(messages);
330
372
  costTracker?.saveLifetime();
373
+ push({ role: "system", content: T.muted("Goodbye 🤖") });
331
374
  setTimeout(exit, 200);
332
375
  return;
333
376
  }
334
377
  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") : ""));
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.`);
337
501
  return;
338
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)`);
518
+ return;
519
+ }
520
+ // ── Standard commands ───────────────────────────────────────────────
339
521
  if (cmd === "price") {
340
522
  const result = await priceFeed.getAiaiaiPriceTool();
341
523
  notify(result.content[0].text);
@@ -384,20 +566,7 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
384
566
  return;
385
567
  }
386
568
  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"));
569
+ notify(["🔥 Burn $AIAIAI", "", `Deposit: ${DEPOSIT_WALLET}`, "Send SOL or USDC here.", "", `Action: ${ACTION_WALLET}`, `Signer: ${SIGNER}`].join("\n"));
401
570
  return;
402
571
  }
403
572
  if (cmd === "actions" || cmd === "activity") {
@@ -410,6 +579,10 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
410
579
  notify(result.content[0].text);
411
580
  return;
412
581
  }
582
+ if (cmd === "keys" || cmd === "providers") {
583
+ notify("Switch to CLI for key management:\n aiaiai keys");
584
+ return;
585
+ }
413
586
  if (cmd === "goals" || cmd === "goal") {
414
587
  const subCmd = args.trim().split(" ")[0];
415
588
  if (subCmd === "add") {
@@ -441,29 +614,23 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
441
614
  notify(result.content[0].text);
442
615
  return;
443
616
  }
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."));
617
+ if (cmd === "update" || cmd === "upgrade") {
618
+ notify("Switch to CLI:\n aiaiai update");
452
619
  return;
453
620
  }
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.") });
621
+ if (cmd === "memory" && args.trim()) {
622
+ const results = memoryStore.search(args.trim(), 8);
623
+ if (results.length === 0) {
624
+ notify(T.muted("No memories found."));
625
+ return;
459
626
  }
460
- else
461
- notify(T.error("No pending approval."));
627
+ const lines = results.map(r => `[${new Date(r.ts).toLocaleDateString()} ${r.role}] ${r.content.slice(0, 120)}`);
628
+ notify(`Memory: "${args.trim()}"\n${lines.join("\n")}`);
462
629
  return;
463
630
  }
464
631
  if (cmd === "gmgn" || cmd === "gmgnhelp") {
465
632
  const gmgnArgs = rest.join(" ");
466
- if (!gmgnArgs || gmgnArgs === "help" || gmgnArgs === "--help") {
633
+ if (!gmgnArgs || gmgnArgs === "help") {
467
634
  notify(gmgnHelp());
468
635
  return;
469
636
  }
@@ -471,58 +638,19 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
471
638
  notify(gmgnStatus());
472
639
  return;
473
640
  }
474
- if (gmgnArgs.startsWith("setup") || gmgnArgs.startsWith("key")) {
475
- notify("GMGN API key setup. Switch to CLI and run: aiaiaichain gmgn setup");
641
+ if (gmgnArgs.startsWith("setup")) {
642
+ notify("Run in CLI: aiaiai gmgn setup");
476
643
  return;
477
644
  }
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])) });
645
+ const result = await gmgnTool("", { subcommand: gmgnArgs.split(" ")[0], chain: "sol" });
483
646
  notify(result.content[0].text);
484
647
  return;
485
648
  }
486
649
  if (cmd === "gmgnmarket" || cmd === "market") {
487
- const query = rest.join(" ");
488
- const result = await gmgnMarketTool("", { query: query || "trending", chain: "sol" });
650
+ const result = await gmgnMarketTool("", { query: args || "trending", chain: "sol" });
489
651
  notify(result.content[0].text);
490
652
  return;
491
653
  }
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"));
502
- return;
503
- }
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"));
514
- return;
515
- }
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()));
520
- return;
521
- }
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")}`);
524
- return;
525
- }
526
654
  const def = registry.getCommand(cmd);
527
655
  if (!def) {
528
656
  notify(T.error(`Unknown: /${cmd} — try /help`));
@@ -552,6 +680,7 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
552
680
  notify(T.error(`Error: ${e.message}`));
553
681
  }
554
682
  }, [registry, exit, push, notify, uiCtx, modelReg, costTracker]);
683
+ // ── Model selector ─────────────────────────────────────────────────────
555
684
  const handleModelSelect = useCallback((modelId) => {
556
685
  setShowModelSelector(false);
557
686
  try {
@@ -584,18 +713,20 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
584
713
  }, [modelReg]);
585
714
  const ctx = getContextBar(sessionRef.current);
586
715
  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
716
  const SIDEBAR_W = 44;
589
717
  const SIDEBAR_MIN = 38;
590
- const actionIcon = (type) => type === "buy" ? "↗" : type === "burn" ? "🔥" : type === "deposit" ? "↓" : "→";
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" ? "🔥" : "→";
591
720
  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 })] }));
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 })] }));
593
722
  }
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) => {
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) => {
595
724
  const col = a.type === "buy" ? AIAIAI_COLORS.success : a.type === "burn" ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
596
725
  const time = new Date(a.timestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
597
726
  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) => {
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) => {
599
730
  const sentColor = n.sentiment > 0.3 ? AIAIAI_COLORS.success : n.sentiment < -0.3 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
600
731
  const title = n.title.length > 30 ? n.title.slice(0, 28) + "…" : n.title;
601
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 {