@aiaiaichain/agent 0.1.5 → 0.1.7

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 (121) hide show
  1. package/dist/api/ExtensionAPI.d.ts +0 -1
  2. package/dist/api/ExtensionAPI.js +3 -7
  3. package/dist/api/Registry.d.ts +0 -1
  4. package/dist/api/Registry.js +54 -57
  5. package/dist/cli.d.ts +0 -1
  6. package/dist/cli.js +684 -685
  7. package/dist/core/AgentDir.d.ts +1 -1
  8. package/dist/core/AgentDir.js +45 -39
  9. package/dist/core/ChainConfig.d.ts +0 -1
  10. package/dist/core/ChainConfig.js +51 -55
  11. package/dist/core/EnvLoader.d.ts +4 -1
  12. package/dist/core/EnvLoader.js +97 -84
  13. package/dist/core/SystemMonitor.d.ts +0 -1
  14. package/dist/core/SystemMonitor.js +72 -85
  15. package/dist/index.d.ts +0 -1
  16. package/dist/index.js +19 -26
  17. package/dist/loader.d.ts +0 -1
  18. package/dist/loader.js +64 -67
  19. package/dist/mcp/entry.d.ts +0 -1
  20. package/dist/mcp/entry.js +3 -6
  21. package/dist/mcp/server.d.ts +0 -1
  22. package/dist/mcp/server.js +152 -156
  23. package/dist/models/CostTracker.d.ts +0 -1
  24. package/dist/models/CostTracker.js +58 -61
  25. package/dist/models/ModelRegistry.d.ts +0 -1
  26. package/dist/models/ModelRegistry.js +195 -155
  27. package/dist/providers/ProviderRegistry.d.ts +0 -1
  28. package/dist/providers/ProviderRegistry.js +33 -36
  29. package/dist/runner/AgentRunner.d.ts +0 -1
  30. package/dist/runner/AgentRunner.js +180 -184
  31. package/dist/runner/ModelClient.d.ts +0 -1
  32. package/dist/runner/ModelClient.js +133 -134
  33. package/dist/runner/SwarmRouter.d.ts +0 -1
  34. package/dist/runner/SwarmRouter.js +18 -22
  35. package/dist/runner/ToolDispatcher.d.ts +0 -1
  36. package/dist/runner/ToolDispatcher.js +30 -33
  37. package/dist/scheduler/AgentScheduler.d.ts +0 -1
  38. package/dist/scheduler/AgentScheduler.js +99 -103
  39. package/dist/session/ContextStore.d.ts +1 -1
  40. package/dist/session/ContextStore.js +76 -78
  41. package/dist/session/GoalManager.d.ts +0 -1
  42. package/dist/session/GoalManager.js +96 -100
  43. package/dist/session/MemoryStore.d.ts +2 -1
  44. package/dist/session/MemoryStore.js +108 -87
  45. package/dist/session/SessionManager.d.ts +5 -4
  46. package/dist/session/SessionManager.js +83 -62
  47. package/dist/session/SessionStore.d.ts +0 -1
  48. package/dist/session/SessionStore.js +112 -116
  49. package/dist/setup/SetupWizard.d.ts +0 -1
  50. package/dist/setup/SetupWizard.js +61 -64
  51. package/dist/tools/CrossTools.d.ts +0 -1
  52. package/dist/tools/CrossTools.js +140 -144
  53. package/dist/tools/GmgnIntegration.d.ts +0 -1
  54. package/dist/tools/GmgnIntegration.js +220 -230
  55. package/dist/tools/MarketSentiment.d.ts +0 -1
  56. package/dist/tools/MarketSentiment.js +213 -195
  57. package/dist/tools/NewsSentiment.d.ts +0 -1
  58. package/dist/tools/NewsSentiment.js +126 -130
  59. package/dist/tools/PriceFeed.d.ts +6 -1
  60. package/dist/tools/PriceFeed.js +201 -133
  61. package/dist/tools/TechnicalAnalysis.d.ts +1 -2
  62. package/dist/tools/TechnicalAnalysis.js +248 -216
  63. package/dist/tools/TechnicalAnalysis.worker.d.ts +25 -0
  64. package/dist/tools/TechnicalAnalysis.worker.js +92 -0
  65. package/dist/tools/TokenCalendar.d.ts +0 -1
  66. package/dist/tools/TokenCalendar.js +63 -68
  67. package/dist/tools/TokenSecurityScanner.d.ts +0 -1
  68. package/dist/tools/TokenSecurityScanner.js +93 -96
  69. package/dist/tools/TransactionSim.d.ts +0 -1
  70. package/dist/tools/TransactionSim.js +65 -71
  71. package/dist/tui/App.d.ts +0 -1
  72. package/dist/tui/App.js +895 -824
  73. package/dist/tui/ModelSelector.d.ts +0 -1
  74. package/dist/tui/ModelSelector.js +46 -49
  75. package/dist/tui/REPL.d.ts +0 -1
  76. package/dist/tui/REPL.js +222 -210
  77. package/dist/tui/Sparkline.d.ts +0 -1
  78. package/dist/tui/Sparkline.js +36 -37
  79. package/dist/tui/StatusBar.d.ts +0 -1
  80. package/dist/tui/StatusBar.js +9 -10
  81. package/dist/tui/ThemePresets.d.ts +0 -1
  82. package/dist/tui/ThemePresets.js +99 -103
  83. package/dist/tui/theme.d.ts +0 -1
  84. package/dist/tui/theme.js +50 -31
  85. package/dist/util/clipboard.d.ts +0 -1
  86. package/dist/util/clipboard.js +16 -20
  87. package/dist/util/commandSuggest.d.ts +0 -1
  88. package/dist/util/commandSuggest.js +34 -38
  89. package/dist/util/confirmation.d.ts +0 -1
  90. package/dist/util/confirmation.js +8 -11
  91. package/dist/util/errorHandler.d.ts +0 -1
  92. package/dist/util/errorHandler.js +20 -23
  93. package/dist/util/errors.d.ts +59 -0
  94. package/dist/util/errors.js +93 -0
  95. package/dist/util/logger.d.ts +0 -1
  96. package/dist/util/logger.js +30 -33
  97. package/dist/util/processManager.d.ts +0 -1
  98. package/dist/util/processManager.js +33 -36
  99. package/dist/util/resilientFetch.d.ts +6 -1
  100. package/dist/util/resilientFetch.js +134 -80
  101. package/dist/util/responseCache.d.ts +0 -1
  102. package/dist/util/responseCache.js +36 -45
  103. package/dist/util/rpc.d.ts +16 -0
  104. package/dist/util/rpc.js +69 -0
  105. package/dist/util/safeLog.d.ts +0 -1
  106. package/dist/util/safeLog.js +52 -53
  107. package/dist/util/scheduler.d.ts +0 -1
  108. package/dist/util/scheduler.js +53 -58
  109. package/dist/util/webhooks.d.ts +0 -1
  110. package/dist/util/webhooks.js +54 -58
  111. package/dist/wallet/ActionFeed.d.ts +3 -3
  112. package/dist/wallet/ActionFeed.js +189 -187
  113. package/dist/wallet/AgentWallet.d.ts +9 -7
  114. package/dist/wallet/AgentWallet.js +121 -141
  115. package/dist/wallet/ProfitTracker.d.ts +0 -1
  116. package/dist/wallet/ProfitTracker.js +71 -74
  117. package/package.json +12 -7
  118. package/scripts/build-esbuild.mjs +40 -0
  119. package/scripts/bundle-dts.mjs +58 -0
  120. package/scripts/minify.mjs +44 -0
  121. package/scripts/postinstall.js +27 -0
package/dist/tui/App.js CHANGED
@@ -1,8 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- /**
3
- * App — root Ink component. Multi-pane TUI with sidebar, system monitor,
4
- * session persistence, and full GMGN command access.
5
- */
2
+
6
3
  import { useState, useCallback, useEffect, useRef, useMemo } from "react";
7
4
  import { Box, Text, useApp, useInput } from "ink";
8
5
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
@@ -19,7 +16,15 @@ import { resolveModelConfig } from "../runner/ModelClient.js";
19
16
  import { priceFeed } from "../tools/PriceFeed.js";
20
17
  import { getNewsTool, getNewsParams, newsFeed } from "../tools/NewsSentiment.js";
21
18
  import { analyzeTAParams, getCandlesParams, getCandlesTool, fullAnalysis } from "../tools/TechnicalAnalysis.js";
22
- import { getFearGreedTool, fearGreedParams, getFundingRatesTool, fundingRatesParams, getBtcMempoolTool, btcMempoolParams, getDefiTvlTool, defiTvlParams, getSolanaStatsTool, solanaStatsParams, } from "../tools/MarketSentiment.js";
19
+
20
+ let _sentimentTools = null;
21
+ async function getSentimentTools() {
22
+ if (_sentimentTools)
23
+ return _sentimentTools;
24
+ const mod = await import("../tools/MarketSentiment.js");
25
+ _sentimentTools = mod;
26
+ return mod;
27
+ }
23
28
  import { contextStore } from "../session/ContextStore.js";
24
29
  import { goalManager } from "../session/GoalManager.js";
25
30
  import { memoryStore } from "../session/MemoryStore.js";
@@ -28,7 +33,14 @@ import { sessionStore } from "../session/SessionStore.js";
28
33
  import { agentWallet, ACTION_WALLET, DEPOSIT_WALLET, SIGNER } from "../wallet/AgentWallet.js";
29
34
  import { actionFeed } from "../wallet/ActionFeed.js";
30
35
  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";
36
+
37
+ let _crossTools = null;
38
+ async function getCrossTools() {
39
+ if (_crossTools)
40
+ return _crossTools;
41
+ _crossTools = await import("../tools/CrossTools.js");
42
+ return _crossTools;
43
+ }
32
44
  import { systemMonitor } from "../core/SystemMonitor.js";
33
45
  import { Sparkline, PriceHistory } from "./Sparkline.js";
34
46
  import { launchCalendarTool, getLaunchSummary } from "../tools/TokenCalendar.js";
@@ -37,831 +49,890 @@ import { getPresetColors, listThemes, setTheme as setThemePreset } from "./Theme
37
49
  import { copyToClipboard, clipboardSupported } from "../util/clipboard.js";
38
50
  import { suggestCommands } from "../util/commandSuggest.js";
39
51
  import { notify as notifyWebhook, hasWebhooks } from "../util/webhooks.js";
52
+ import { watchTokenTool, removeWatchTool, listWatchTool, checkAlertsTool, addAlertTool, compareTokensTool, portfolioTool, getWatchList as _getWatchList } from "../tools/CrossTools.js";
40
53
  function getContextBar(session) {
41
- if (!session)
42
- return { pct: 0, bar: "░".repeat(20), color: AIAIAI_COLORS.dim };
43
- const pressure = session.getContextPressure();
44
- const filled = Math.round(pressure.pct / 5);
45
- const color = pressure.level === "critical" ? AIAIAI_COLORS.error
46
- : pressure.level === "red" ? AIAIAI_COLORS.warn
47
- : pressure.level === "yellow" ? AIAIAI_COLORS.header
48
- : AIAIAI_COLORS.success;
49
- return { pct: pressure.pct, bar: "█".repeat(Math.min(filled, 20)) + "░".repeat(Math.max(0, 20 - filled)), color };
54
+ if (!session)
55
+ return { pct: 0, bar: "░".repeat(20), color: AIAIAI_COLORS.dim };
56
+ const pressure = session.getContextPressure();
57
+ const filled = Math.round(pressure.pct / 5);
58
+ const color = pressure.level === "critical" ? AIAIAI_COLORS.error
59
+ : pressure.level === "red" ? AIAIAI_COLORS.warn
60
+ : pressure.level === "yellow" ? AIAIAI_COLORS.header
61
+ : AIAIAI_COLORS.success;
62
+ return { pct: pressure.pct, bar: "█".repeat(Math.min(filled, 20)) + "░".repeat(Math.max(0, 20 - filled)), color };
50
63
  }
51
64
  function highlightJson(obj, indent = 0) {
52
- const pad = " ".repeat(indent);
53
- if (obj === null)
54
- return T.dim("null");
55
- if (typeof obj === "boolean")
56
- return T.warn(String(obj));
57
- if (typeof obj === "number")
58
- return T.success(String(obj));
59
- if (typeof obj === "string")
60
- return T.accent(`"${obj}"`);
61
- if (Array.isArray(obj)) {
62
- if (obj.length === 0)
63
- return "[]";
64
- if (obj.length <= 3)
65
- return `[${obj.map(v => highlightJson(v, 0)).join(", ")}]`;
66
- return `[\n${obj.slice(0, 5).map(v => `${pad} ${highlightJson(v, indent + 1)}`).join(",\n")}${obj.length > 5 ? `\n${pad} …${obj.length - 5} more` : ""}\n${pad}]`;
67
- }
68
- if (typeof obj === "object") {
69
- const entries = Object.entries(obj);
70
- if (entries.length === 0)
71
- return "{}";
72
- return `{\n${entries.slice(0, 8).map(([k, v]) => `${pad} ${T.accent(k)}: ${highlightJson(v, indent + 1)}`).join(",\n")}${entries.length > 8 ? `\n${pad} …${entries.length - 8} more` : ""}\n${pad}}`;
73
- }
74
- return String(obj);
65
+ const pad = " ".repeat(indent);
66
+ if (obj === null)
67
+ return T.dim("null");
68
+ if (typeof obj === "boolean")
69
+ return T.warn(String(obj));
70
+ if (typeof obj === "number")
71
+ return T.success(String(obj));
72
+ if (typeof obj === "string")
73
+ return T.accent(`"${obj}"`);
74
+ if (Array.isArray(obj)) {
75
+ if (obj.length === 0)
76
+ return "[]";
77
+ if (obj.length <= 3)
78
+ return `[${obj.map(v => highlightJson(v, 0)).join(", ")}]`;
79
+ return `[\n${obj.slice(0, 5).map(v => `${pad} ${highlightJson(v, indent + 1)}`).join(",\n")}${obj.length > 5 ? `\n${pad} …${obj.length - 5} more` : ""}\n${pad}]`;
80
+ }
81
+ if (typeof obj === "object") {
82
+ const entries = Object.entries(obj);
83
+ if (entries.length === 0)
84
+ return "{}";
85
+ return `{\n${entries.slice(0, 8).map(([k, v]) => `${pad} ${T.accent(k)}: ${highlightJson(v, indent + 1)}`).join(",\n")}${entries.length > 8 ? `\n${pad} …${entries.length - 8} more` : ""}\n${pad}}`;
86
+ }
87
+ return String(obj);
75
88
  }
76
89
  function formatToolContent(text) {
77
- try {
78
- return highlightJson(JSON.parse(text));
79
- }
80
- catch {
81
- return text.length > 300 ? text.slice(0, 300) + T.dim("\n…[truncated]") : text;
82
- }
90
+ try {
91
+ return highlightJson(JSON.parse(text));
92
+ }
93
+ catch {
94
+ return text.length > 300 ? text.slice(0, 300) + T.dim("\n…[truncated]") : text;
95
+ }
83
96
  }
84
97
  let _msgIdCounter = 0;
85
98
  function nextId() { return String(++_msgIdCounter); }
99
+
100
+ const _interned = new Map();
101
+ function intern(s) {
102
+ const existing = _interned.get(s);
103
+ if (existing)
104
+ return existing;
105
+ _interned.set(s, s);
106
+ return s;
107
+ }
86
108
  const sessionId = `session-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
87
109
  export function App({ registry, systemPrompt, chain: initialChain = "solana", modelReg, costTracker, onNotifyReady, onStatusReady, onModelSelectorReady, }) {
88
- const { exit } = useApp();
89
- const [messages, setMessages] = useState([]);
90
- const [streaming, setStreaming] = useState("");
91
- const [toolRunning, setToolRunning] = useState(null);
92
- const [disabled, setDisabled] = useState(false);
93
- const [chain] = useState(initialChain);
94
- const [statusBadges, setStatusBadges] = useState({});
95
- const [priceBadge, setPriceBadge] = useState("");
96
- const [costBadge, setCostBadge] = useState("");
97
- const [newsBadge, setNewsBadge] = useState("");
98
- const [sidebarNews, setSidebarNews] = useState([]);
99
- const [aiaiPrice, setAiaiPrice] = useState(null);
100
- const [coldBalance, setColdBalance] = useState({ sol: 0, aiaiai: 0, usdc: 0 });
101
- const [actionBalance, setActionBalance] = useState({ sol: 0, aiaiai: 0, usdc: 0 });
102
- const [depositBalance, setDepositBalance] = useState({ sol: 0, aiaiai: 0, usdc: 0 });
103
- const [recentActions, setRecentActions] = useState([]);
104
- const [fees, setFees] = useState({ buyFees: 0, burnFees: 0, total: 0 });
105
- const [showModelSelector, setShowModelSelector] = useState(false);
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));
113
- const runnerRef = useRef(null);
114
- const sessionRef = useRef(null);
115
- const sessionCtxRef = useRef(null);
116
- const messagesRef = useRef([]);
117
- const theme = makeTheme();
118
- let modelName = "no-model";
119
- try {
120
- modelName = resolveModelConfig(modelReg).model;
121
- }
122
- catch { /* shown via banner */ }
123
- const push = useCallback((msg) => {
124
- setMessages(prev => {
125
- const next = [...prev, { ...msg, id: nextId(), ts: Date.now() }];
126
- messagesRef.current = next;
127
- return next;
128
- });
129
- }, []);
130
- const notify = useCallback((content) => { push({ role: "notify", content }); }, [push]);
131
- const setStatus = useCallback((key, value) => {
132
- setStatusBadges(prev => ({ ...prev, [key]: value }));
133
- }, []);
134
- const uiCtx = {
135
- notify, setStatus,
136
- setTheme() { }, setHeader() { },
137
- showModelSelector: (query) => {
138
- setModelSelectorInitialQuery(query ?? "");
139
- setShowModelSelector(true);
140
- },
141
- theme,
142
- };
143
- // ── Register ALL tools ──────────────────────────────────────────────────
144
- function registerBuiltinTools() {
145
- if (!modelReg)
146
- return;
147
- const mk = modelReg;
148
- registry.addTool({ name: "list_models", label: "List Models", description: "Search and filter available AI models.", parameters: mk.listModelsParams, execute: (id, p) => mk.listModelsTool(id, p) });
149
- registry.addTool({ name: "pick_model", label: "Pick Model", description: "Find a model in a given tier.", parameters: mk.pickModelParams, execute: (id, p) => mk.pickModelTool(id, p) });
150
- registry.addTool({ name: "model_summary", label: "Model Summary", description: "Summary of available model tiers.", parameters: mk.summaryParams, execute: () => mk.summaryTool() });
151
- if (costTracker) {
152
- registry.addTool({ name: "cost_report", label: "Cost Report", description: "Show session and lifetime token usage.", parameters: costTracker.costReportParams, execute: () => costTracker.costReportTool() });
153
- }
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) => {
159
- const closes = p.prices;
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 }));
161
- const results = fullAnalysis(candles);
162
- const summary = results.find((r) => r.indicator === "SUMMARY");
163
- const text = results.map((r) => {
164
- const s = r.signal === "bullish" ? "🟢" : r.signal === "bearish" ? "🔴" : "⚪";
165
- const v = Array.isArray(r.value) ? `[${r.value.length}]` : r.value;
166
- return `${s} ${r.indicator}: ${v}`;
167
- }).join("\n");
168
- return { content: [{ type: "text", text: `Technical Analysis:\n${text}` }], details: { results, overall_signal: summary?.signal } };
169
- } });
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() });
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) });
180
- registry.addTool({ name: "list_tasks", label: "List Tasks", description: "List saved task contexts.", parameters: Type.Object({}), execute: () => contextStore.listTasksTool() });
181
- registry.addTool({ name: "set_goal", label: "Set Goal", description: "Set a persistent goal.", parameters: goalManager.setGoalParams, execute: (id, p) => goalManager.setGoalTool(id, p) });
182
- registry.addTool({ name: "complete_goal", label: "Complete Goal", description: "Mark a goal complete.", parameters: goalManager.completeGoalParams, execute: (id, p) => goalManager.completeGoalTool(id, p) });
183
- registry.addTool({ name: "list_goals", label: "List Goals", description: "List goals.", parameters: goalManager.listGoalsParams, execute: (id, p) => goalManager.listGoalsTool(id, p) });
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) });
185
- registry.addTool({ name: "schedule_task", label: "Schedule Task", description: "Schedule a recurring task.", parameters: agentScheduler.addTaskParams, execute: (id, p) => agentScheduler.addTaskTool(id, p) });
186
- registry.addTool({ name: "list_schedule", label: "List Schedule", description: "List scheduled tasks.", parameters: agentScheduler.listTasksParams, execute: (id, p) => agentScheduler.listTasksTool(id, p) });
187
- registry.addTool({ name: "remove_schedule", label: "Remove Schedule", description: "Remove a scheduled task.", parameters: agentScheduler.removeTaskParams, execute: (id, p) => agentScheduler.removeTaskTool(id, p) });
188
- // GMGN tools
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) });
199
- }
200
- // ── Main effect: setup + intervals ──────────────────────────────────────
201
- useEffect(() => {
202
- registerBuiltinTools();
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
- });
224
- // Initial wallet fetches
225
- agentWallet.getAll().then(w => {
226
- setColdBalance({ sol: w.cold.sol, aiaiai: w.cold.aiaiai, usdc: w.cold.usdc });
227
- setActionBalance({ sol: w.action.sol, aiaiai: w.action.aiaiai, usdc: w.action.usdc });
228
- setDepositBalance({ sol: w.deposit.sol, aiaiai: w.deposit.aiaiai, usdc: w.deposit.usdc });
229
- }).catch(() => { });
230
- // Initial actions
231
- actionFeed.refresh().then(() => {
232
- setRecentActions(actionFeed.getRecentActions(8).map(a => ({ type: a.type, amount: a.amount, usdValue: a.usdValue, timestamp: a.timestamp })));
233
- setFees(actionFeed.getFees());
234
- }).catch(() => { });
235
- // Initial news
236
- newsFeed.getLatest().then(report => {
237
- setSidebarNews(report.items.slice(0, 6).map(i => ({ title: i.title, sentiment: i.sentiment ?? 0, source: i.source })));
238
- setNewsBadge(newsFeed.statusBadge());
239
- }).catch(() => { });
240
- // Load watch list
241
- setWatchList(_getWatchList());
242
- // Scheduler
243
- agentScheduler.start((task) => {
244
- push({ role: "system", content: T.muted(`⏰ Scheduled: "${task.name}"`) });
245
- if (!disabled && runnerRef.current) {
246
- setDisabled(true);
247
- setStreaming("");
248
- runnerRef.current.run(`[SCHEDULED TASK: ${task.name}] ${task.prompt}`)
249
- .catch((e) => notify(T.error(`Scheduler error: ${e.message}`)));
250
- }
251
- });
252
- // Price interval (30s)
253
- const priceInterval = setInterval(() => {
254
- priceFeed.getAiaiaiPrice().then(p => {
255
- setAiaiPrice({ priceUsd: p.priceUsd, change: p.priceChange24h, mcap: p.marketCap, liq: p.liquidityUsd });
256
- if (p.priceUsd) {
257
- setPriceBadge(`$${parseFloat(p.priceUsd).toFixed(6)}`);
258
- priceHistoryRef.current.push(parseFloat(p.priceUsd));
259
- }
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
- }
270
- }).catch(() => { });
271
- if (costTracker)
272
- setCostBadge(costTracker.statusLine());
273
- }, 30_000);
274
- // Wallet + actions interval (60s)
275
- const walletInterval = setInterval(() => {
276
- agentWallet.getAll().then(w => {
277
- setColdBalance({ sol: w.cold.sol, aiaiai: w.cold.aiaiai, usdc: w.cold.usdc });
278
- setActionBalance({ sol: w.action.sol, aiaiai: w.action.aiaiai, usdc: w.action.usdc });
279
- setDepositBalance({ sol: w.deposit.sol, aiaiai: w.deposit.aiaiai, usdc: w.deposit.usdc });
280
- }).catch(() => { });
281
- actionFeed.refresh().then(() => {
282
- setRecentActions(actionFeed.getRecentActions(8).map(a => ({ type: a.type, amount: a.amount, usdValue: a.usdValue, timestamp: a.timestamp })));
283
- setFees(actionFeed.getFees());
284
- }).catch(() => { });
285
- }, 60_000);
286
- // News interval (5min)
287
- const newsInterval = setInterval(() => {
288
- newsFeed.getLatest().then(report => {
289
- setSidebarNews(report.items.slice(0, 6).map(i => ({ title: i.title, sentiment: i.sentiment ?? 0, source: i.source })));
290
- setNewsBadge(newsFeed.statusBadge());
291
- }).catch(() => { });
292
- }, 300_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);
308
- const session = new SessionManager();
309
- session.setSystemPrompt(registry.getSystemPrompt() || systemPrompt || "You are AIAIAI Chain Agent.");
310
- sessionRef.current = session;
311
- const sessionCtx = { ui: uiCtx, hasUI: true, config: { OPENROUTER_API_KEY: process.env.OPENROUTER_API_KEY, DEFAULT_MODEL: process.env.DEFAULT_MODEL } };
312
- sessionCtxRef.current = sessionCtx;
313
- registry.fireHook("session_start", sessionCtx).then(() => {
314
- if (memoryStore.isAvailable) {
315
- const memCtx = memoryStore.buildContextBlock(sessionId);
316
- if (memCtx)
317
- session.setSystemPrompt((session.getSystemPrompt() || "") + memCtx);
318
- }
319
- const goalCtx = goalManager.buildContextBlock();
320
- if (goalCtx)
321
- session.setSystemPrompt((session.getSystemPrompt() || "") + goalCtx);
322
- push({ role: "system", content: T.muted("Session started. Type /help for commands.") });
323
- });
324
- const runner = new AgentRunner(registry, session, (event) => {
325
- if (event.type === "text_delta")
326
- setStreaming(prev => prev + event.text);
327
- else if (event.type === "tool_start")
328
- setToolRunning(event.name);
329
- else if (event.type === "tool_done") {
330
- setToolRunning(null);
331
- push({ role: "tool", content: formatToolContent(event.result), toolName: event.name, isError: event.isError });
332
- }
333
- else if (event.type === "turn_done") {
334
- setDisabled(false);
335
- setToolRunning(null);
336
- setStreaming(prev => {
337
- if (prev.trim()) {
338
- push({ role: "assistant", content: prev });
339
- memoryStore.save(sessionId, "assistant", prev);
340
- }
341
- return "";
342
- });
343
- }
344
- else if (event.type === "error") {
345
- setDisabled(false);
346
- setToolRunning(null);
347
- setStreaming("");
348
- notify(T.error(`Error: ${event.message}`));
349
- }
350
- else if (event.type === "approval_request") {
351
- setToolRunning(null);
352
- const { toolName, args, approve } = event;
353
- let parsed = "";
354
- try {
355
- parsed = JSON.stringify(JSON.parse(args), null, 2).slice(0, 200);
356
- }
357
- catch {
358
- parsed = args.slice(0, 200);
359
- }
360
- push({ role: "notify", content: `⚠️ APPROVAL REQUIRED\n\nTool: ${toolName}\nArgs: ${parsed}\n\nType /approve or /deny to continue.` });
361
- runnerRef.current._pendingApproval = approve;
362
- }
363
- }, sessionCtx, "normal", modelReg, costTracker, goalManager, contextStore);
364
- runnerRef.current = runner;
365
- if (costTracker)
366
- setCostBadge(costTracker.statusLine());
367
- return () => {
368
- if (sessionCtxRef.current)
369
- registry.fireHook("session_end", sessionCtxRef.current).catch(() => { });
370
- agentScheduler.stop();
371
- sessionStore.stopAutoSave();
372
- if (messages.length > 0)
373
- sessionStore.save(messages);
374
- clearInterval(priceInterval);
375
- clearInterval(walletInterval);
376
- clearInterval(newsInterval);
377
- clearInterval(sysInterval);
378
- clearInterval(saveInterval);
379
- costTracker?.saveLifetime();
380
- };
381
- }, []);
382
- useInput((_input, key) => {
383
- if (key.ctrl && _input === "c") {
384
- push({ role: "system", content: T.muted("Goodbye 🤖") });
385
- if (messages.length > 0)
386
- sessionStore.save(messages);
387
- costTracker?.saveLifetime();
388
- setTimeout(exit, 200);
389
- }
390
- });
391
- // ── Submit handler ─────────────────────────────────────────────────────
392
- const handleSubmit = useCallback(async (raw) => {
393
- const input = raw.trim();
394
- if (!input)
395
- return;
396
- if (input.startsWith("/")) {
397
- const [cmd, ...rest] = input.slice(1).split(" ");
398
- const args = rest.join(" ");
399
- if (cmd === "exit" || cmd === "quit") {
400
- if (messages.length > 0)
401
- sessionStore.save(messages);
402
- costTracker?.saveLifetime();
403
- push({ role: "system", content: T.muted("Goodbye 🤖") });
404
- setTimeout(exit, 200);
405
- return;
406
- }
407
- if (cmd === "help") {
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);
490
- return;
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 ───────────────────────────────────────────────
551
- if (cmd === "price") {
552
- const result = await priceFeed.getAiaiaiPriceTool();
553
- notify(result.content[0].text);
554
- return;
555
- }
556
- if (cmd === "news") {
557
- const result = await getNewsTool("", { limit: 15 });
558
- notify(result.content[0].text);
559
- return;
560
- }
561
- if (cmd === "models") {
562
- if (!modelReg) {
563
- notify(T.error("Model registry not available"));
564
- return;
565
- }
566
- const result = await modelReg.listModelsTool("", { query: rest.join(" ") || undefined, limit: 20 });
567
- notify(result.content[0].text);
568
- return;
569
- }
570
- if (cmd === "cost") {
571
- if (!costTracker) {
572
- notify(T.error("Cost tracker not available"));
573
- return;
574
- }
575
- const result = await costTracker.costReportTool();
576
- notify(result.content[0].text);
577
- return;
578
- }
579
- if (cmd === "model") {
580
- setModelSelectorInitialQuery(args);
581
- setShowModelSelector(true);
582
- return;
583
- }
584
- if (cmd === "clear") {
585
- setMessages([]);
586
- return;
587
- }
588
- if (cmd === "wallet") {
589
- const result = await agentWallet.getAgentBalanceTool();
590
- notify(result.content[0].text);
591
- return;
592
- }
593
- if (cmd === "deposit") {
594
- const result = await agentWallet.getDepositBalanceTool();
595
- notify(result.content[0].text);
596
- return;
597
- }
598
- if (cmd === "burn") {
599
- notify(["🔥 Burn $AIAIAI", "", `Deposit: ${DEPOSIT_WALLET}`, "Send SOL or USDC here.", "", `Action: ${ACTION_WALLET}`, `Signer: ${SIGNER}`].join("\n"));
600
- return;
601
- }
602
- if (cmd === "actions" || cmd === "activity") {
603
- const result = await actionFeed.getActionsTool("", { limit: 15 });
604
- notify(result.content[0].text);
605
- return;
606
- }
607
- if (cmd === "fees") {
608
- const result = await actionFeed.getFeesTool();
609
- notify(result.content[0].text);
610
- return;
611
- }
612
- if (cmd === "keys" || cmd === "providers") {
613
- notify("Switch to CLI for key management:\n aiaiai keys");
614
- return;
615
- }
616
- if (cmd === "goals" || cmd === "goal") {
617
- const subCmd = args.trim().split(" ")[0];
618
- if (subCmd === "add") {
619
- const text = args.trim().slice(4).trim();
620
- if (!text) {
621
- notify(T.error("Usage: /goal add <text>"));
622
- return;
623
- }
624
- const result = await goalManager.setGoalTool("", { text });
625
- notify(result.content[0].text);
626
- }
627
- else if (subCmd === "done" || subCmd === "complete") {
628
- const id = args.trim().split(" ")[1];
629
- if (!id) {
630
- notify(T.error("Usage: /goal done <id>"));
631
- return;
632
- }
633
- const result = await goalManager.completeGoalTool("", { id });
634
- notify(result.content[0].text);
635
- }
636
- else {
637
- const result = await goalManager.listGoalsTool("", { status: "all" });
638
- notify(result.content[0].text);
639
- }
640
- return;
641
- }
642
- if (cmd === "schedule" || cmd === "sched") {
643
- const result = await agentScheduler.listTasksTool("", { enabled_only: false });
644
- notify(result.content[0].text);
645
- return;
646
- }
647
- if (cmd === "update" || cmd === "upgrade") {
648
- notify("Switch to CLI:\n aiaiai update");
649
- return;
650
- }
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;
656
- }
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")}`);
659
- return;
660
- }
661
- if (cmd === "gmgn" || cmd === "gmgnhelp") {
662
- const gmgnArgs = rest.join(" ");
663
- if (!gmgnArgs || gmgnArgs === "help") {
664
- notify(gmgnHelp());
665
- return;
666
- }
667
- if (gmgnArgs === "status") {
668
- notify(gmgnStatus());
669
- return;
670
- }
671
- if (gmgnArgs.startsWith("setup")) {
672
- notify("Run in CLI: aiaiai gmgn setup");
673
- return;
674
- }
675
- const result = await gmgnTool("", { subcommand: gmgnArgs.split(" ")[0], chain: "sol" });
676
- notify(result.content[0].text);
677
- return;
678
- }
679
- if (cmd === "gmgnmarket" || cmd === "market") {
680
- const result = await gmgnMarketTool("", { query: args || "trending", chain: "sol" });
681
- notify(result.content[0].text);
682
- return;
683
- }
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);
688
- return;
689
- }
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"));
710
- return;
711
- }
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>"));
733
- return;
734
- }
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());
772
- return;
773
- }
774
- const def = registry.getCommand(cmd);
775
- if (!def) {
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}`));
781
- return;
782
- }
783
- try {
784
- await def.handler(args, { ui: uiCtx });
785
- }
786
- catch (e) {
787
- notify(T.error(`Error: ${e.message}`));
788
- }
789
- return;
790
- }
791
- if (!runnerRef.current) {
792
- notify(T.error("Agent not ready"));
793
- return;
794
- }
795
- push({ role: "user", content: input });
796
- memoryStore.save(sessionId, "user", input);
797
- setDisabled(true);
798
- setStreaming("");
799
- try {
800
- await runnerRef.current.run(input);
801
- }
802
- catch (e) {
803
- setDisabled(false);
804
- notify(T.error(`Error: ${e.message}`));
805
- }
806
- }, [registry, exit, push, notify, uiCtx, modelReg, costTracker]);
807
- // ── Model selector ─────────────────────────────────────────────────────
808
- const handleModelSelect = useCallback((modelId) => {
809
- setShowModelSelector(false);
810
- try {
811
- const AIAIAI_HOME = process.env.AIAIAI_HOME ?? join(homedir(), '.aiaiai');
812
- const envFile = join(AIAIAI_HOME, ".env");
813
- mkdirSync(AIAIAI_HOME, { recursive: true });
814
- const content = existsSync(envFile) ? readFileSync(envFile, "utf-8") : "";
815
- const re = /^DEFAULT_MODEL=.*$/m;
816
- const line = `DEFAULT_MODEL=${modelId}`;
817
- writeFileSync(envFile, re.test(content) ? content.replace(re, line) : content + "\n" + line + "\n", "utf-8");
818
- process.env.DEFAULT_MODEL = modelId;
819
- }
820
- catch { /* non-fatal */ }
821
- notify(T.accent(`Model set to: ${modelId}\nRestart to apply.`));
822
- }, [notify]);
823
- const handleModelCancel = useCallback(() => { setShowModelSelector(false); }, []);
824
- const modelList = useMemo(() => {
825
- if (!modelReg)
826
- return [];
827
- const tiers = ["orchestrator", "analyst", "worker", "free"];
828
- const items = [];
829
- for (const tier of tiers) {
830
- const pool = modelReg.getPool(tier);
831
- for (const tm of pool) {
832
- if (tm.available && tm.failures < 3)
833
- items.push({ id: tm.model.id, tier });
834
- }
835
- }
836
- return items;
837
- }, [modelReg]);
838
- const ctx = getContextBar(sessionRef.current);
839
- const statusLine = [costBadge, newsBadge, ...Object.values(statusBadges)].filter(Boolean).join(" ") || null;
840
- const SIDEBAR_W = 44;
841
- const SIDEBAR_MIN = 38;
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) => {
846
- const col = a.type === "buy" ? AIAIAI_COLORS.success : a.type === "burn" ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
847
- const time = new Date(a.timestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
848
- return _jsxs(Text, { color: col, children: [actionIcon(a.type), " ", time, " ", a.type, " ", a.amount.toLocaleString()] }, 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) => {
852
- const sentColor = n.sentiment > 0.3 ? AIAIAI_COLORS.success : n.sentiment < -0.3 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
853
- const title = n.title.length > 30 ? n.title.slice(0, 28) + "…" : n.title;
854
- return _jsx(Text, { color: sentColor, children: title }, i);
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: () => {
856
- runnerRef.current?.abort();
857
- setDisabled(false);
858
- setToolRunning(null);
859
- setStreaming(prev => {
860
- if (prev.trim())
861
- push({ role: "assistant", content: prev + " — [interrupted]" });
862
- return "";
863
- });
864
- push({ role: "system", content: T.dim("— stream interrupted —") });
865
- } }) })] }))] }));
866
- }
867
- //# sourceMappingURL=App.js.map
110
+ const { exit } = useApp();
111
+
112
+ const [messages, setMessages] = useState([]);
113
+ const [streaming, setStreaming] = useState("");
114
+ const [toolRunning, setToolRunning] = useState(null);
115
+ const [disabled, setDisabled] = useState(false);
116
+ const [chain] = useState(initialChain);
117
+ const [showModelSelector, setShowModelSelector] = useState(false);
118
+ const [modelSelectorInitialQuery, setModelSelectorInitialQuery] = useState("");
119
+
120
+ const [badges, setBadges] = useState({
121
+ status: {},
122
+ price: "",
123
+ cost: "",
124
+ news: "",
125
+ });
126
+
127
+ const [sidebar, setSidebar] = useState({
128
+ news: [],
129
+ price: null,
130
+ watchList: [],
131
+ });
132
+
133
+ const [wallets, setWallets] = useState({
134
+ cold: { sol: 0, aiaiai: 0, usdc: 0 },
135
+ action: { sol: 0, aiaiai: 0, usdc: 0 },
136
+ deposit: { sol: 0, aiaiai: 0, usdc: 0 },
137
+ });
138
+
139
+ const [activity, setActivity] = useState({
140
+ recent: [],
141
+ fees: { buyFees: 0, burnFees: 0, total: 0 },
142
+ });
143
+
144
+ const [sysStats, setSysStats] = useState({ cpu: 0, ram: 0, uptime: "0m", apiCalls: 0 });
145
+ const priceHistoryRef = useRef(new PriceHistory(40));
146
+ const runnerRef = useRef(null);
147
+ const sessionRef = useRef(null);
148
+ const sessionCtxRef = useRef(null);
149
+ const messagesRef = useRef([]);
150
+ const theme = makeTheme();
151
+ let modelName = "no-model";
152
+ try {
153
+ modelName = resolveModelConfig(modelReg).model;
154
+ }
155
+ catch { }
156
+ const push = useCallback((msg) => {
157
+ setMessages(prev => {
158
+ const next = [...prev, { ...msg, id: nextId(), ts: Date.now() }];
159
+ messagesRef.current = next;
160
+ return next;
161
+ });
162
+ }, []);
163
+ const notify = useCallback((content) => { push({ role: "notify", content }); }, [push]);
164
+ const setStatus = useCallback((key, value) => {
165
+ setBadges(prev => ({ ...prev, status: { ...prev.status, [key]: value } }));
166
+ }, []);
167
+ const uiCtx = {
168
+ notify, setStatus,
169
+ setTheme() { }, setHeader() { },
170
+ showModelSelector: (query) => {
171
+ setModelSelectorInitialQuery(query ?? "");
172
+ setShowModelSelector(true);
173
+ },
174
+ theme,
175
+ };
176
+
177
+ function registerBuiltinTools() {
178
+ if (!modelReg)
179
+ return;
180
+ const mk = modelReg;
181
+ registry.addTool({ name: "list_models", label: "List Models", description: "Search and filter available AI models.", parameters: mk.listModelsParams, execute: (id, p) => mk.listModelsTool(id, p) });
182
+ registry.addTool({ name: "pick_model", label: "Pick Model", description: "Find a model in a given tier.", parameters: mk.pickModelParams, execute: (id, p) => mk.pickModelTool(id, p) });
183
+ registry.addTool({ name: "model_summary", label: "Model Summary", description: "Summary of available model tiers.", parameters: mk.summaryParams, execute: () => mk.summaryTool() });
184
+ if (costTracker) {
185
+ registry.addTool({ name: "cost_report", label: "Cost Report", description: "Show session and lifetime token usage.", parameters: costTracker.costReportParams, execute: () => costTracker.costReportTool() });
186
+ }
187
+ registry.addTool({ name: "get_aiaiai_price", label: "AIAIAI Price", description: "Get $AIAIAI price, liquidity, MCap, volume.", parameters: priceFeed.getAiaiaiPriceParams, execute: () => priceFeed.getAiaiaiPriceTool() });
188
+ 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) });
189
+ registry.addTool({ name: "get_news", label: "Get News", description: "Crypto news with sentiment.", parameters: getNewsParams, execute: (id, p) => getNewsTool(id, p) });
190
+ registry.addTool({ name: "get_candles", label: "Get Candles", description: "OHLCV from Binance + TA.", parameters: getCandlesParams, execute: (id, p) => getCandlesTool(id, p) });
191
+ registry.addTool({ name: "analyze_ta", label: "Technical Analysis", description: "RSI, MACD, Bollinger, ATR.", parameters: analyzeTAParams, execute: async (_id, p) => {
192
+ const closes = p.prices;
193
+ 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 }));
194
+ const results = await fullAnalysis(candles);
195
+ const summary = results.find((r) => r.indicator === "SUMMARY");
196
+ const text = results.map((r) => {
197
+ const s = r.signal === "bullish" ? "🟢" : r.signal === "bearish" ? "🔴" : "⚪";
198
+ const v = Array.isArray(r.value) ? `[${r.value.length}]` : r.value;
199
+ return `${s} ${r.indicator}: ${v}`;
200
+ }).join("\n");
201
+ return { content: [{ type: "text", text: `Technical Analysis:\n${text}` }], details: { results, overall_signal: summary?.signal } };
202
+ } });
203
+
204
+ registry.addTool({ name: "get_fear_greed", label: "Fear & Greed", description: "Crypto Fear & Greed Index.", parameters: {}, execute: async () => { const t = await getSentimentTools(); return t.getFearGreedTool(); } });
205
+ registry.addTool({ name: "get_funding_rates", label: "Funding Rates", description: "Binance funding rates.", parameters: {}, execute: async (_id, p) => { const t = await getSentimentTools(); return t.getFundingRatesTool(_id, p); } });
206
+ registry.addTool({ name: "get_btc_mempool", label: "BTC Mempool", description: "BTC mempool stats.", parameters: {}, execute: async () => { const t = await getSentimentTools(); return t.getBtcMempoolTool(); } });
207
+ registry.addTool({ name: "get_defi_tvl", label: "DeFi TVL", description: "DeFiLlama TVL.", parameters: {}, execute: async (_id, p) => { const t = await getSentimentTools(); return t.getDefiTvlTool(_id, p); } });
208
+ registry.addTool({ name: "get_solana_stats", label: "Solana Stats", description: "Solana epoch, slot, SOL price.", parameters: {}, execute: async () => { const t = await getSentimentTools(); return t.getSolanaStatsTool(); } });
209
+ registry.addTool({ name: "get_agent_balance", label: "Agent Balance", description: "Cold + action wallet balances.", parameters: agentWallet.getAgentBalanceParams, execute: () => agentWallet.getAgentBalanceTool() });
210
+ registry.addTool({ name: "get_deposit_balance", label: "Deposit Balance", description: "Deposit wallet + instructions.", parameters: agentWallet.getDepositBalanceParams, execute: () => agentWallet.getDepositBalanceTool() });
211
+ registry.addTool({ name: "get_actions", label: "Agent Actions", description: "Recent buy/burn actions.", parameters: actionFeed.getActionsParams, execute: (id, p) => actionFeed.getActionsTool(id, p) });
212
+ registry.addTool({ name: "get_fees", label: "Agent Fees", description: "Accumulated fees.", parameters: actionFeed.getFeesParams, execute: () => actionFeed.getFeesTool() });
213
+ 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) });
214
+ registry.addTool({ name: "list_tasks", label: "List Tasks", description: "List saved task contexts.", parameters: {}, execute: () => contextStore.listTasksTool() });
215
+ registry.addTool({ name: "set_goal", label: "Set Goal", description: "Set a persistent goal.", parameters: goalManager.setGoalParams, execute: (id, p) => goalManager.setGoalTool(id, p) });
216
+ registry.addTool({ name: "complete_goal", label: "Complete Goal", description: "Mark a goal complete.", parameters: goalManager.completeGoalParams, execute: (id, p) => goalManager.completeGoalTool(id, p) });
217
+ registry.addTool({ name: "list_goals", label: "List Goals", description: "List goals.", parameters: goalManager.listGoalsParams, execute: (id, p) => goalManager.listGoalsTool(id, p) });
218
+ 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) });
219
+ registry.addTool({ name: "schedule_task", label: "Schedule Task", description: "Schedule a recurring task.", parameters: agentScheduler.addTaskParams, execute: (id, p) => agentScheduler.addTaskTool(id, p) });
220
+ registry.addTool({ name: "list_schedule", label: "List Schedule", description: "List scheduled tasks.", parameters: agentScheduler.listTasksParams, execute: (id, p) => agentScheduler.listTasksTool(id, p) });
221
+ registry.addTool({ name: "remove_schedule", label: "Remove Schedule", description: "Remove a scheduled task.", parameters: agentScheduler.removeTaskParams, execute: (id, p) => agentScheduler.removeTaskTool(id, p) });
222
+
223
+ 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) });
224
+ registry.addTool({ name: "gmgn_market", label: "GMGN Market Data", description: "Market data: trending, trenches, kline, signals.", parameters: gmgnMarketToolParams, execute: (id, p) => gmgnMarketTool(id, p) });
225
+
226
+ registry.addTool({ name: "watch_token", label: "Watch Token", description: "Add token to watch list.", parameters: {}, execute: async (_id, p) => { const t = await getCrossTools(); return t.watchTokenTool(_id, p); } });
227
+ registry.addTool({ name: "unwatch_token", label: "Unwatch Token", description: "Remove token from watch list.", parameters: {}, execute: async (_id, p) => { const t = await getCrossTools(); return t.removeWatchTool(_id, p); } });
228
+ registry.addTool({ name: "watch_list", label: "Watch List", description: "Show watched tokens.", parameters: {}, execute: async () => { const t = await getCrossTools(); return t.listWatchTool(); } });
229
+ registry.addTool({ name: "add_alert", label: "Price Alert", description: "Set a price alert.", parameters: {}, execute: async (_id, p) => { const t = await getCrossTools(); return t.addAlertTool(_id, p); } });
230
+ registry.addTool({ name: "check_alerts", label: "Check Alerts", description: "List active alerts.", parameters: {}, execute: async () => { const t = await getCrossTools(); return t.checkAlertsTool(); } });
231
+ registry.addTool({ name: "compare_tokens", label: "Compare Tokens", description: "Side-by-side token comparison.", parameters: {}, execute: async (_id, p) => { const t = await getCrossTools(); return t.compareTokensTool(_id, p); } });
232
+ registry.addTool({ name: "portfolio", label: "Portfolio", description: "Wallet portfolio analysis.", parameters: {}, execute: async (_id, p) => { const t = await getCrossTools(); return t.portfolioTool(_id, p); } });
233
+ }
234
+
235
+ useEffect(() => {
236
+ registerBuiltinTools();
237
+
238
+ (async () => {
239
+
240
+ const fetchPriceWithRetry = async (retries = 3, delay = 2000) => {
241
+ for (let i = 0; i < retries; i++) {
242
+ try {
243
+ const p = await priceFeed.getAiaiaiPrice();
244
+ setSidebar(prev => ({ ...prev, price: { priceUsd: p.priceUsd, change: p.priceChange24h, mcap: p.marketCap, liq: p.liquidityUsd } }));
245
+ if (p.priceUsd)
246
+ setBadges(prev => ({ ...prev, price: `$${parseFloat(p.priceUsd).toFixed(6)}` }));
247
+ actionFeed.setPrice(parseFloat(p.priceUsd || "0.0004"));
248
+ return;
249
+ }
250
+ catch (e) {
251
+ if (i === retries - 1)
252
+ throw e;
253
+ await new Promise(r => setTimeout(r, delay * (i + 1)));
254
+ }
255
+ }
256
+ };
257
+ fetchPriceWithRetry().catch(() => {
258
+ console.error('Initial price fetch failed after retries');
259
+ });
260
+
261
+ agentWallet.getAll().then(w => {
262
+ setWallets({
263
+ cold: { sol: w.cold.sol, aiaiai: w.cold.aiaiai, usdc: w.cold.usdc },
264
+ action: { sol: w.action.sol, aiaiai: w.action.aiaiai, usdc: w.action.usdc },
265
+ deposit: { sol: w.deposit.sol, aiaiai: w.deposit.aiaiai, usdc: w.deposit.usdc },
266
+ });
267
+ }).catch(() => { });
268
+
269
+ actionFeed.refresh().then(() => {
270
+ setActivity({
271
+ recent: actionFeed.getRecentActions(8).map(a => ({ type: a.type, amount: a.amount, usdValue: a.usdValue, timestamp: a.timestamp })),
272
+ fees: actionFeed.getFees(),
273
+ });
274
+ }).catch(() => { });
275
+
276
+ newsFeed.getLatest().then(report => {
277
+ setSidebar(prev => ({ ...prev, news: report.items.slice(0, 6).map(i => ({ title: i.title, sentiment: i.sentiment ?? 0, source: i.source })) }));
278
+ setBadges(prev => ({ ...prev, news: newsFeed.statusBadge() }));
279
+ }).catch(() => { });
280
+
281
+ getCrossTools().then(({ getWatchList: _getWatchList }) => {
282
+ setSidebar(prev => ({ ...prev, watchList: _getWatchList() }));
283
+ });
284
+
285
+ agentScheduler.start((task) => {
286
+ push({ role: "system", content: T.muted(`⏰ Scheduled: "${task.name}"`) });
287
+ if (!disabled && runnerRef.current) {
288
+ setDisabled(true);
289
+ setStreaming("");
290
+ runnerRef.current.run(`[SCHEDULED TASK: ${task.name}] ${task.prompt}`)
291
+ .catch((e) => notify(T.error(`Scheduler error: ${e.message}`)));
292
+ }
293
+ });
294
+
295
+ const priceInterval = setInterval(() => {
296
+ priceFeed.getAiaiaiPrice().then(p => {
297
+ setSidebar(prev => ({ ...prev, price: { priceUsd: p.priceUsd, change: p.priceChange24h, mcap: p.marketCap, liq: p.liquidityUsd } }));
298
+ if (p.priceUsd) {
299
+ setBadges(prev => ({ ...prev, price: `$${parseFloat(p.priceUsd).toFixed(6)}` }));
300
+ priceHistoryRef.current.push(parseFloat(p.priceUsd));
301
+ }
302
+ actionFeed.setPrice(parseFloat(p.priceUsd || "0.0004"));
303
+ systemMonitor.trackApiCall();
304
+
305
+ if (p.priceUsd) {
306
+ getCrossTools().then(({ checkAlerts: _checkAlerts }) => {
307
+ const triggered = _checkAlerts(parseFloat(p.priceUsd), "AIAIAI");
308
+ for (const alert of triggered) {
309
+ notify(`🔔 Alert triggered: ${alert.type} $${alert.price} — $AIAIAI is now $${p.priceUsd}`);
310
+ notifyWebhook(`🔔 **Alert**: ${alert.type} $${alert.price} — $AIAIAI now $${p.priceUsd}`).catch(() => { });
311
+ }
312
+ });
313
+ }
314
+ }).catch(() => { });
315
+ if (costTracker)
316
+ setBadges(prev => ({ ...prev, cost: costTracker.statusLine() }));
317
+ }, 30_000);
318
+
319
+ const walletInterval = setInterval(() => {
320
+ agentWallet.getAll().then(w => {
321
+ setWallets({
322
+ cold: { sol: w.cold.sol, aiaiai: w.cold.aiaiai, usdc: w.cold.usdc },
323
+ action: { sol: w.action.sol, aiaiai: w.action.aiaiai, usdc: w.action.usdc },
324
+ deposit: { sol: w.deposit.sol, aiaiai: w.deposit.aiaiai, usdc: w.deposit.usdc },
325
+ });
326
+ }).catch(() => { });
327
+ actionFeed.refresh().then(() => {
328
+ setActivity({
329
+ recent: actionFeed.getRecentActions(8).map(a => ({ type: a.type, amount: a.amount, usdValue: a.usdValue, timestamp: a.timestamp })),
330
+ fees: actionFeed.getFees(),
331
+ });
332
+ }).catch(() => { });
333
+ }, 60_000);
334
+
335
+ const newsInterval = setInterval(() => {
336
+ newsFeed.getLatest().then(report => {
337
+ setSidebar(prev => ({ ...prev, news: report.items.slice(0, 6).map(i => ({ title: i.title, sentiment: i.sentiment ?? 0, source: i.source })) }));
338
+ setBadges(prev => ({ ...prev, news: newsFeed.statusBadge() }));
339
+ }).catch(() => { });
340
+ }, 300_000);
341
+
342
+ const sysInterval = setInterval(() => {
343
+ const stats = systemMonitor.getStats();
344
+ setSysStats({ cpu: stats.cpuPercent, ram: stats.ramPercent, uptime: stats.uptime, apiCalls: systemMonitor.resetApiCounter() });
345
+ }, 10_000);
346
+
347
+ sessionStore.startAutoSave(() => messagesRef.current);
348
+ sessionStore.setCurrentId(sessionId);
349
+ const saveInterval = setInterval(() => {
350
+ if (messagesRef.current.length > 0)
351
+ sessionStore.save(messagesRef.current);
352
+ }, 30_000);
353
+ const session = new SessionManager();
354
+ session.setSystemPrompt(registry.getSystemPrompt() || systemPrompt || "You are AIAIAI Chain Agent.");
355
+ sessionRef.current = session;
356
+ const sessionCtx = { ui: uiCtx, hasUI: true, config: { OPENROUTER_API_KEY: process.env.OPENROUTER_API_KEY, DEFAULT_MODEL: process.env.DEFAULT_MODEL } };
357
+ sessionCtxRef.current = sessionCtx;
358
+ registry.fireHook("session_start", sessionCtx).then(() => {
359
+ if (memoryStore.isAvailable) {
360
+ const memCtx = memoryStore.buildContextBlock(sessionId);
361
+ if (memCtx)
362
+ session.setSystemPrompt((session.getSystemPrompt() || "") + memCtx);
363
+ }
364
+ const goalCtx = goalManager.buildContextBlock();
365
+ if (goalCtx)
366
+ session.setSystemPrompt((session.getSystemPrompt() || "") + goalCtx);
367
+ push({ role: "system", content: T.muted("Session started. Type /help for commands.") });
368
+ });
369
+ const runner = new AgentRunner(registry, session, (event) => {
370
+ if (event.type === "text_delta")
371
+ setStreaming(prev => prev + event.text);
372
+ else if (event.type === "tool_start")
373
+ setToolRunning(event.name);
374
+ else if (event.type === "tool_done") {
375
+ setToolRunning(null);
376
+ push({ role: "tool", content: formatToolContent(event.result), toolName: event.name, isError: event.isError });
377
+ }
378
+ else if (event.type === "turn_done") {
379
+ setDisabled(false);
380
+ setToolRunning(null);
381
+ setStreaming(prev => {
382
+ if (prev.trim()) {
383
+ push({ role: "assistant", content: prev });
384
+ memoryStore.save(sessionId, "assistant", prev);
385
+ }
386
+ return "";
387
+ });
388
+ }
389
+ else if (event.type === "error") {
390
+ setDisabled(false);
391
+ setToolRunning(null);
392
+ setStreaming("");
393
+ notify(T.error(`Error: ${event.message}`));
394
+ }
395
+ else if (event.type === "approval_request") {
396
+ setToolRunning(null);
397
+ const { toolName, args, approve } = event;
398
+ let parsed = "";
399
+ try {
400
+ parsed = JSON.stringify(JSON.parse(args), null, 2).slice(0, 200);
401
+ }
402
+ catch {
403
+ parsed = args.slice(0, 200);
404
+ }
405
+ push({ role: "notify", content: `⚠️ APPROVAL REQUIRED\n\nTool: ${toolName}\nArgs: ${parsed}\n\nType /approve or /deny to continue.` });
406
+ runnerRef.current._pendingApproval = approve;
407
+ }
408
+ }, sessionCtx, "normal", modelReg, costTracker, goalManager, contextStore);
409
+ runnerRef.current = runner;
410
+ if (costTracker)
411
+ setBadges(prev => ({ ...prev, cost: costTracker.statusLine() }));
412
+ return () => {
413
+ if (sessionCtxRef.current)
414
+ registry.fireHook("session_end", sessionCtxRef.current).catch(() => { });
415
+ agentScheduler.stop();
416
+ sessionStore.stopAutoSave();
417
+ if (messages.length > 0)
418
+ sessionStore.save(messages);
419
+ clearInterval(priceInterval);
420
+ clearInterval(walletInterval);
421
+ clearInterval(newsInterval);
422
+ clearInterval(sysInterval);
423
+ clearInterval(saveInterval);
424
+ costTracker?.saveLifetime();
425
+ };
426
+ })();
427
+ }, []);
428
+ useInput((_input, key) => {
429
+ if (key.ctrl && _input === "c") {
430
+ push({ role: "system", content: T.muted("Goodbye 🤖") });
431
+ if (messages.length > 0)
432
+ sessionStore.save(messages);
433
+ costTracker?.saveLifetime();
434
+ setTimeout(exit, 200);
435
+ }
436
+ });
437
+
438
+ const handleSubmit = useCallback(async (rawInput) => {
439
+ let input = rawInput.trim();
440
+ if (!input)
441
+ return;
442
+
443
+ if (input.length > 4096) {
444
+ notify(T.error("Input too long (max 4KB). Try a shorter message."));
445
+ return;
446
+ }
447
+
448
+ const cleaned = input.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
449
+ if (cleaned !== input) {
450
+ notify(T.warn("⚠️ Control characters removed from input."));
451
+ input = cleaned;
452
+ }
453
+
454
+ const injectionPatterns = [
455
+ /ignore (all |previous |above|prior) instructions/i,
456
+ /you are now /i,
457
+ /new instructions/i,
458
+ /override (system|safety|rules)/i,
459
+ /forget (all |everything|previous)/i,
460
+ /\[system\]/i,
461
+ /<\/system>/i,
462
+ ];
463
+ for (const pattern of injectionPatterns) {
464
+ if (pattern.test(input)) {
465
+ notify(T.error("⚠️ Blocked: Input contains suspicious patterns."));
466
+ return;
467
+ }
468
+ }
469
+ if (input.startsWith("/")) {
470
+ const [cmd, ...rest] = input.slice(1).split(" ");
471
+ const args = rest.join(" ");
472
+ if (cmd === "exit" || cmd === "quit") {
473
+ if (messages.length > 0)
474
+ sessionStore.save(messages);
475
+ costTracker?.saveLifetime();
476
+ push({ role: "system", content: T.muted("Goodbye 🤖") });
477
+ setTimeout(exit, 200);
478
+ return;
479
+ }
480
+ if (cmd === "help") {
481
+ 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");
482
+ return;
483
+ }
484
+
485
+ if (cmd === "token") {
486
+
487
+ const parts = args.split(" ");
488
+ const subcmd = parts[0];
489
+ if (!subcmd || subcmd === "help") {
490
+ 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>");
491
+ return;
492
+ }
493
+ const chain = parts[parts.indexOf("--chain") + 1] || "sol";
494
+ const address = parts[parts.indexOf("--address") + 1];
495
+ if (!address && subcmd !== "help") {
496
+ notify(T.error("--address is required"));
497
+ return;
498
+ }
499
+ const result = await gmgnTool("", { subcommand: subcmd, chain, address });
500
+ notify(result.content[0].text);
501
+ return;
502
+ }
503
+ if (cmd === "track") {
504
+
505
+ const parts = args.split(" ");
506
+ const subcmd = parts[0];
507
+ if (!subcmd || subcmd === "help") {
508
+ notify("Track commands:\n/track kol --chain <sol|bsc|base|eth>\n/track smartmoney --chain <chain>\n/track follow-wallet --chain <chain>");
509
+ return;
510
+ }
511
+ const chain = parts[parts.indexOf("--chain") + 1] || "sol";
512
+ const result = await gmgnTool("", { subcommand: `track ${subcmd}`, chain, limit: 10 });
513
+ notify(result.content[0].text);
514
+ return;
515
+ }
516
+ if (cmd === "market") {
517
+
518
+ const parts = args.split(" ");
519
+ const subcmd = parts[0];
520
+ if (!subcmd || subcmd === "help") {
521
+ 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>");
522
+ return;
523
+ }
524
+ const chain = parts[parts.indexOf("--chain") + 1] || "sol";
525
+ const address = parts[parts.indexOf("--address") + 1];
526
+ const resolution = parts[parts.indexOf("--resolution") + 1];
527
+ const interval = parts[parts.indexOf("--interval") + 1];
528
+ const trenchesType = parts[parts.indexOf("--type") + 1];
529
+ const toolParams = { subcommand: `market ${subcmd}`, chain };
530
+ if (address)
531
+ toolParams.address = address;
532
+ if (resolution)
533
+ toolParams.resolution = resolution;
534
+ if (interval)
535
+ toolParams.interval = interval;
536
+ if (trenchesType)
537
+ toolParams.type = trenchesType;
538
+ const result = await gmgnTool("", toolParams);
539
+ notify(result.content[0].text);
540
+ return;
541
+ }
542
+
543
+ if (cmd === "watch") {
544
+ const result = await watchTokenTool("", { address: args || "AVPJS61gZmWKtaEpb7qYPKo8Fk2xQUsayYQxPiPMpump" });
545
+ setSidebar(prev => ({ ...prev, watchList: _getWatchList() }));
546
+ notify(result.content[0].text);
547
+ return;
548
+ }
549
+ if (cmd === "unwatch") {
550
+ const result = await removeWatchTool("", { address: args });
551
+ setSidebar(prev => ({ ...prev, watchList: _getWatchList() }));
552
+ notify(result.content[0].text);
553
+ return;
554
+ }
555
+ if (cmd === "watchlist") {
556
+ const result = await listWatchTool();
557
+ notify(result.content[0].text);
558
+ return;
559
+ }
560
+ if (cmd === "alerts") {
561
+ const result = await checkAlertsTool();
562
+ notify(result.content[0].text);
563
+ return;
564
+ }
565
+ if (cmd === "alert") {
566
+
567
+ const parts = args.split(" ");
568
+ const type = parts[0];
569
+ const price = parseFloat(parts[1]);
570
+ if (!type || isNaN(price)) {
571
+ notify(T.error("Usage: /alert above|below <price>"));
572
+ return;
573
+ }
574
+ const result = await addAlertTool("", { token: "AIAIAI", type, price });
575
+ notify(result.content[0].text);
576
+ return;
577
+ }
578
+ if (cmd === "compare") {
579
+
580
+ const parts = args.split(" ");
581
+ if (parts.length < 2) {
582
+ notify(T.error("Usage: /compare <address1> <address2>"));
583
+ return;
584
+ }
585
+ const result = await compareTokensTool("", { address1: parts[0], address2: parts[1] });
586
+ notify(result.content[0].text);
587
+ return;
588
+ }
589
+ if (cmd === "portfolio") {
590
+ const addr = args || "A11iZoqEt6hU7HyggqC67ee4AtYmaJjwKCvJLerJRV2J";
591
+ const result = await portfolioTool("", { address: addr });
592
+ notify(result.content[0].text);
593
+ return;
594
+ }
595
+
596
+ if (cmd === "sessions") {
597
+ const sessions = sessionStore.list();
598
+ if (sessions.length === 0) {
599
+ notify("No saved sessions.");
600
+ return;
601
+ }
602
+ const lines = sessions.slice(0, 10).map((s, i) => ` ${i + 1}. ${s.title} (${s.messageCount} msgs, ${new Date(s.updatedAt).toLocaleDateString()})`);
603
+ notify(`Recent Sessions:\n${lines.join("\n")}\n\nUse /resume <number> to resume.`);
604
+ return;
605
+ }
606
+ if (cmd === "resume") {
607
+ const sessions = sessionStore.list();
608
+ const idx = parseInt(args) - 1;
609
+ if (isNaN(idx) || idx < 0 || idx >= sessions.length) {
610
+ notify(T.error("Invalid session number."));
611
+ return;
612
+ }
613
+ const session = sessionStore.load(sessions[idx].id);
614
+ if (!session) {
615
+ notify(T.error("Session not found."));
616
+ return;
617
+ }
618
+ setMessages(session.messages);
619
+ sessionStore.setCurrentId(session.id);
620
+ notify(`Resumed: ${session.title} (${session.messages.length} messages)`);
621
+ return;
622
+ }
623
+
624
+ if (cmd === "price") {
625
+ const result = await priceFeed.getAiaiaiPriceTool();
626
+ notify(result.content[0].text);
627
+ return;
628
+ }
629
+ if (cmd === "news") {
630
+ const result = await getNewsTool("", { limit: 15 });
631
+ notify(result.content[0].text);
632
+ return;
633
+ }
634
+ if (cmd === "models") {
635
+ if (!modelReg) {
636
+ notify(T.error("Model registry not available"));
637
+ return;
638
+ }
639
+ const result = await modelReg.listModelsTool("", { query: rest.join(" ") || undefined, limit: 20 });
640
+ notify(result.content[0].text);
641
+ return;
642
+ }
643
+ if (cmd === "cost") {
644
+ if (!costTracker) {
645
+ notify(T.error("Cost tracker not available"));
646
+ return;
647
+ }
648
+ const result = await costTracker.costReportTool();
649
+ notify(result.content[0].text);
650
+ return;
651
+ }
652
+ if (cmd === "model") {
653
+ setModelSelectorInitialQuery(args);
654
+ setShowModelSelector(true);
655
+ return;
656
+ }
657
+ if (cmd === "clear") {
658
+ setMessages([]);
659
+ return;
660
+ }
661
+ if (cmd === "wallet") {
662
+ const result = await agentWallet.getAgentBalanceTool();
663
+ notify(result.content[0].text);
664
+ return;
665
+ }
666
+ if (cmd === "deposit") {
667
+ const result = await agentWallet.getDepositBalanceTool();
668
+ notify(result.content[0].text);
669
+ return;
670
+ }
671
+ if (cmd === "burn") {
672
+ notify(["🔥 Burn $AIAIAI", "", `Deposit: ${DEPOSIT_WALLET}`, "Send SOL or USDC here.", "", `Action: ${ACTION_WALLET}`, `Signer: ${SIGNER}`].join("\n"));
673
+ return;
674
+ }
675
+ if (cmd === "actions" || cmd === "activity") {
676
+ const result = await actionFeed.getActionsTool("", { limit: 15 });
677
+ notify(result.content[0].text);
678
+ return;
679
+ }
680
+ if (cmd === "fees") {
681
+ const result = await actionFeed.getFeesTool();
682
+ notify(result.content[0].text);
683
+ return;
684
+ }
685
+ if (cmd === "keys" || cmd === "providers") {
686
+ notify("Switch to CLI for key management:\n aiaiai keys");
687
+ return;
688
+ }
689
+ if (cmd === "goals" || cmd === "goal") {
690
+ const subCmd = args.trim().split(" ")[0];
691
+ if (subCmd === "add") {
692
+ const text = args.trim().slice(4).trim();
693
+ if (!text) {
694
+ notify(T.error("Usage: /goal add <text>"));
695
+ return;
696
+ }
697
+ const result = await goalManager.setGoalTool("", { text });
698
+ notify(result.content[0].text);
699
+ }
700
+ else if (subCmd === "done" || subCmd === "complete") {
701
+ const id = args.trim().split(" ")[1];
702
+ if (!id) {
703
+ notify(T.error("Usage: /goal done <id>"));
704
+ return;
705
+ }
706
+ const result = await goalManager.completeGoalTool("", { id });
707
+ notify(result.content[0].text);
708
+ }
709
+ else {
710
+ const result = await goalManager.listGoalsTool("", { status: "all" });
711
+ notify(result.content[0].text);
712
+ }
713
+ return;
714
+ }
715
+ if (cmd === "schedule" || cmd === "sched") {
716
+ const result = await agentScheduler.listTasksTool("", { enabled_only: false });
717
+ notify(result.content[0].text);
718
+ return;
719
+ }
720
+ if (cmd === "update" || cmd === "upgrade") {
721
+ notify("Switch to CLI:\n aiaiai update");
722
+ return;
723
+ }
724
+ if (cmd === "memory" && args.trim()) {
725
+ const results = memoryStore.search(args.trim(), 8);
726
+ if (results.length === 0) {
727
+ notify(T.muted("No memories found."));
728
+ return;
729
+ }
730
+ const lines = results.map(r => `[${new Date(r.ts).toLocaleDateString()} ${r.role}] ${r.content.slice(0, 120)}`);
731
+ notify(`Memory: "${args.trim()}"\n${lines.join("\n")}`);
732
+ return;
733
+ }
734
+ if (cmd === "gmgn" || cmd === "gmgnhelp") {
735
+ const gmgnArgs = rest.join(" ");
736
+ if (!gmgnArgs || gmgnArgs === "help") {
737
+ notify(gmgnHelp());
738
+ return;
739
+ }
740
+ if (gmgnArgs === "status") {
741
+ notify(gmgnStatus());
742
+ return;
743
+ }
744
+ if (gmgnArgs.startsWith("setup")) {
745
+ notify("Run in CLI: aiaiai gmgn setup");
746
+ return;
747
+ }
748
+ const result = await gmgnTool("", { subcommand: gmgnArgs.split(" ")[0], chain: "sol" });
749
+ notify(result.content[0].text);
750
+ return;
751
+ }
752
+ if (cmd === "gmgnmarket" || cmd === "market") {
753
+ const result = await gmgnMarketTool("", { query: args || "trending", chain: "sol" });
754
+ notify(result.content[0].text);
755
+ return;
756
+ }
757
+
758
+ if (cmd === "launches" || cmd === "calendar") {
759
+ const result = await launchCalendarTool("", { chain: args || "solana", limit: 15 });
760
+ notify(result.content[0].text);
761
+ return;
762
+ }
763
+ if (cmd === "diff" || cmd === "compared") {
764
+ const sessions = sessionStore.list();
765
+ if (sessions.length < 2) {
766
+ notify("Need at least 2 saved sessions to compare. Start a new session with /exit and restart.");
767
+ return;
768
+ }
769
+ const latest = sessions[0];
770
+ const prev = sessions[1];
771
+ if (!latest || !prev) {
772
+ notify("Could not load sessions for comparison.");
773
+ return;
774
+ }
775
+ const lines = [
776
+ `📊 Session Diff`,
777
+ `Current: ${latest.title} (${latest.messageCount} msgs)`,
778
+ `Previous: ${prev.title} (${prev.messageCount} msgs)`,
779
+ `Difference: ${latest.messageCount - prev.messageCount} messages`,
780
+ `Last active: ${new Date(latest.updatedAt).toLocaleString()}`,
781
+ ];
782
+ notify(lines.join("\n"));
783
+ return;
784
+ }
785
+ if (cmd === "copy" && args.trim()) {
786
+ const ok = copyToClipboard(args.trim());
787
+ notify(ok ? T.success(`✓ Copied to clipboard: ${args.slice(0, 30)}…`) : T.error("Clipboard not supported in this terminal. Try: " + clipboardSupported()));
788
+ return;
789
+ }
790
+ if (cmd === "theme") {
791
+ if (!args || args === "list") {
792
+ const current = getPresetColors();
793
+ notify(`🎨 Themes\n${listThemes()}\n\nApply: /theme <name>`);
794
+ }
795
+ else if (setThemePreset(args)) {
796
+ notify(T.success(`✓ Theme set to "${args}". Restart to apply.`));
797
+ }
798
+ else {
799
+ notify(T.error(`Unknown theme "${args}". Try: /theme list`));
800
+ }
801
+ return;
802
+ }
803
+ if (cmd === "simulate" || cmd === "sim") {
804
+ if (!args.trim()) {
805
+ notify(T.error("Usage: /simulate <base64-tx>"));
806
+ return;
807
+ }
808
+ const result = await simulateTxTool("", { transaction: args.trim() });
809
+ notify(result.content[0].text);
810
+ return;
811
+ }
812
+ if (cmd === "explain") {
813
+ if (!modelReg) {
814
+ notify(T.error("Model registry not available"));
815
+ return;
816
+ }
817
+ const lastAssistant = [...messages].reverse().find(m => m.role === "tool");
818
+ if (!lastAssistant) {
819
+ notify("No tool result to explain. Run a tool first.");
820
+ return;
821
+ }
822
+ push({ role: "user", content: `Explain this tool result in simple terms:\n${lastAssistant.content.slice(0, 2000)}` });
823
+ setDisabled(true);
824
+ setStreaming("");
825
+ try {
826
+ await runnerRef.current.run(`Explain this tool result simply:\n${lastAssistant.content.slice(0, 2000)}`);
827
+ }
828
+ catch (e) {
829
+ setDisabled(false);
830
+ notify(T.error(`Error: ${e.message}`));
831
+ }
832
+ return;
833
+ }
834
+ if (cmd === "webhook" || cmd === "webhooks") {
835
+ if (hasWebhooks()) {
836
+ notify("✅ Webhooks configured\n\nUsage: /webhook test\nConfig: WEBHOOK_DISCORD / WEBHOOK_TELEGRAM in ~/.aiaiai/.env");
837
+ }
838
+ else {
839
+ notify("No webhooks configured.\nAdd WEBHOOK_DISCORD=... or WEBHOOK_TELEGRAM=... to ~/.aiaiai/.env");
840
+ }
841
+ return;
842
+ }
843
+ if (cmd === "clipboard") {
844
+ notify(clipboardSupported());
845
+ return;
846
+ }
847
+ const def = registry.getCommand(cmd);
848
+ if (!def) {
849
+ const suggestions = suggestCommands(cmd);
850
+ const tip = suggestions.length > 0
851
+ ? `\nDid you mean: ${suggestions.map(s => `/${s}`).join(", ")}?`
852
+ : "";
853
+ notify(T.error(`Unknown: /${cmd} try /help${tip}`));
854
+ return;
855
+ }
856
+ try {
857
+ await def.handler(args, { ui: uiCtx });
858
+ }
859
+ catch (e) {
860
+ notify(T.error(`Error: ${e.message}`));
861
+ }
862
+ return;
863
+ }
864
+ if (!runnerRef.current) {
865
+ notify(T.error("Agent not ready"));
866
+ return;
867
+ }
868
+ push({ role: "user", content: input });
869
+ memoryStore.save(sessionId, "user", input);
870
+ setDisabled(true);
871
+ setStreaming("");
872
+ try {
873
+ await runnerRef.current.run(input);
874
+ }
875
+ catch (e) {
876
+ setDisabled(false);
877
+ notify(T.error(`Error: ${e.message}`));
878
+ }
879
+ }, [registry, exit, push, notify, uiCtx, modelReg, costTracker]);
880
+
881
+ const handleModelSelect = useCallback((modelId) => {
882
+ setShowModelSelector(false);
883
+ try {
884
+ const AIAIAI_HOME = process.env.AIAIAI_HOME ?? join(homedir(), '.aiaiai');
885
+ const envFile = join(AIAIAI_HOME, ".env");
886
+ mkdirSync(AIAIAI_HOME, { recursive: true });
887
+ const content = existsSync(envFile) ? readFileSync(envFile, "utf-8") : "";
888
+ const re = /^DEFAULT_MODEL=.*$/m;
889
+ const line = `DEFAULT_MODEL=${modelId}`;
890
+ writeFileSync(envFile, re.test(content) ? content.replace(re, line) : content + "\n" + line + "\n", "utf-8");
891
+ process.env.DEFAULT_MODEL = modelId;
892
+ }
893
+ catch { }
894
+ notify(T.accent(`Model set to: ${modelId}\nRestart to apply.`));
895
+ }, [notify]);
896
+ const handleModelCancel = useCallback(() => { setShowModelSelector(false); }, []);
897
+ const modelList = useMemo(() => {
898
+ if (!modelReg)
899
+ return [];
900
+ const tiers = ["orchestrator", "analyst", "worker", "free"];
901
+ const items = [];
902
+ for (const tier of tiers) {
903
+ const pool = modelReg.getPool(tier);
904
+ for (const tm of pool) {
905
+ if (tm.available && tm.failures < 3)
906
+ items.push({ id: tm.model.id, tier });
907
+ }
908
+ }
909
+ return items;
910
+ }, [modelReg]);
911
+ const ctx = getContextBar(sessionRef.current);
912
+ const statusLine = [badges.cost, badges.news, ...Object.values(badges.status)].filter(Boolean).join(" ") || null;
913
+ const SIDEBAR_W = 44;
914
+ const SIDEBAR_MIN = 38;
915
+ const changeColor = (pct) => pct > 0 ? AIAIAI_COLORS.success : pct < 0 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
916
+ const actionIcon = (type) => type === "buy" ? "↗" : type === "burn" ? "🔥" : "→";
917
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusBar, { model: modelName, chain: chain, price: badges.price, toolRunning: toolRunning, connected: true, statusLine: statusLine, cpu: sysStats.cpu, ram: sysStats.ram, uptime: sysStats.uptime, apiCalls: sysStats.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: 40, 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" }), sidebar.price ? (_jsxs(_Fragment, { children: [_jsxs(Text, { color: changeColor(sidebar.price.change), children: [sidebar.price.priceUsd ? `$${parseFloat(sidebar.price.priceUsd).toFixed(8)}` : "N/A", " ", sidebar.price.change > 0 ? "▲" : sidebar.price.change < 0 ? "▼" : "─", Math.abs(sidebar.price.change).toFixed(1), "%"] }), _jsx(Sparkline, { data: priceHistoryRef.current.getData(), width: 38, color: changeColor(sidebar.price.change), label: "" }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["MCap: ", sidebar.price.mcap ? `$${(sidebar.price.mcap / 1000).toFixed(1)}k` : "N/A"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Liq: $", (sidebar.price.liq / 1000).toFixed(1), "k"] })] })) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Loading..." })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDCBC Action" }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["SOL: ", wallets.action.sol.toFixed(2)] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["AIAIAI: ", wallets.action.aiaiai.toLocaleString(undefined, { maximumFractionDigits: 0 })] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDCB0 Deposit" }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["USDC: ", wallets.deposit.usdc.toFixed(2)] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["SOL: ", wallets.deposit.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" }), activity.recent.length > 0 ? activity.recent.map((a, i) => {
918
+ const col = a.type === "buy" ? AIAIAI_COLORS.success : a.type === "burn" ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
919
+ const time = new Date(a.timestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
920
+ return _jsxs(Text, { color: col, children: [actionIcon(a.type), " ", time, " ", a.type, " ", a.amount.toLocaleString()] }, i);
921
+ }) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Waiting..." })] }), _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: $", activity.fees.buyFees.toFixed(4)] }), _jsxs(Text, { color: AIAIAI_COLORS.accent, bold: true, children: ["Total: $", activity.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" }), sidebar.watchList.length > 0 ? sidebar.watchList.slice(0, 4).map((addr, i) => {
922
+ return _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [addr.slice(0, 8), "\u2026", addr.slice(-6)] }, i);
923
+ }) : _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" }), sidebar.news.length > 0 ? sidebar.news.slice(0, 4).map((n, i) => {
924
+ const sentColor = n.sentiment > 0.3 ? AIAIAI_COLORS.success : n.sentiment < -0.3 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
925
+ const title = n.title.length > 30 ? n.title.slice(0, 28) + "..." : n.title;
926
+ return _jsx(Text, { color: sentColor, children: title }, i);
927
+ }) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Loading..." })] }), _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: () => {
928
+ runnerRef.current?.abort();
929
+ setDisabled(false);
930
+ setToolRunning(null);
931
+ setStreaming(prev => {
932
+ if (prev.trim())
933
+ push({ role: "assistant", content: prev + " — [interrupted]" });
934
+ return "";
935
+ });
936
+ push({ role: "system", content: T.dim("— stream interrupted —") });
937
+ } }) })] }))] }));
938
+ }