@jellyos/agent 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/README.npm.md +212 -0
- package/bin/jellyos-mcp +26 -0
- package/dist/api/ExtensionAPI.d.ts +6 -0
- package/dist/api/Registry.js +3 -1
- package/dist/cli.js +117 -42
- package/dist/index.d.ts +24 -1
- package/dist/index.js +19 -2
- package/dist/mcp/entry.d.ts +2 -0
- package/dist/mcp/entry.js +71 -0
- package/dist/mcp/server.d.ts +31 -0
- package/dist/mcp/server.js +128 -0
- package/dist/models/CostTracker.d.ts +66 -0
- package/dist/models/CostTracker.js +148 -0
- package/dist/models/ModelRegistry.d.ts +157 -0
- package/dist/models/ModelRegistry.js +496 -0
- package/dist/models/index.d.ts +5 -0
- package/dist/models/index.js +3 -0
- package/dist/runner/AgentRunner.d.ts +23 -2
- package/dist/runner/AgentRunner.js +264 -24
- package/dist/runner/ModelClient.d.ts +26 -6
- package/dist/runner/ModelClient.js +147 -28
- package/dist/runner/SwarmRouter.d.ts +10 -7
- package/dist/runner/SwarmRouter.js +85 -28
- package/dist/runner/ToolDispatcher.d.ts +10 -0
- package/dist/runner/ToolDispatcher.js +106 -2
- package/dist/scheduler/AgentScheduler.d.ts +118 -0
- package/dist/scheduler/AgentScheduler.js +253 -0
- package/dist/session/ContextStore.d.ts +96 -0
- package/dist/session/ContextStore.js +207 -0
- package/dist/session/GoalManager.d.ts +101 -0
- package/dist/session/GoalManager.js +167 -0
- package/dist/session/MemoryStore.d.ts +48 -0
- package/dist/session/MemoryStore.js +166 -0
- package/dist/session/SessionManager.d.ts +45 -4
- package/dist/session/SessionManager.js +151 -8
- package/dist/telemetry/Tracer.d.ts +48 -0
- package/dist/telemetry/Tracer.js +102 -0
- package/dist/tests/ContextStore.test.d.ts +2 -0
- package/dist/tests/ContextStore.test.js +74 -0
- package/dist/tests/ModelRegistry.test.d.ts +2 -0
- package/dist/tests/ModelRegistry.test.js +69 -0
- package/dist/tests/SessionManager.test.d.ts +2 -0
- package/dist/tests/SessionManager.test.js +108 -0
- package/dist/tests/TechnicalAnalysis.test.d.ts +2 -0
- package/dist/tests/TechnicalAnalysis.test.js +109 -0
- package/dist/tools/MarketSentiment.d.ts +166 -0
- package/dist/tools/MarketSentiment.js +209 -0
- package/dist/tools/NewsSentiment.d.ts +67 -0
- package/dist/tools/NewsSentiment.js +226 -0
- package/dist/tools/PriceFeed.d.ts +105 -0
- package/dist/tools/PriceFeed.js +282 -0
- package/dist/tools/TechnicalAnalysis.d.ts +110 -0
- package/dist/tools/TechnicalAnalysis.js +357 -0
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.js +4 -0
- package/dist/tui/App.d.ts +7 -5
- package/dist/tui/App.js +350 -65
- package/dist/tui/REPL.d.ts +2 -1
- package/dist/tui/REPL.js +11 -6
- package/dist/tui/StatusBar.js +1 -1
- package/package.json +9 -4
- package/dist/api/ExtensionAPI.d.ts.map +0 -1
- package/dist/api/ExtensionAPI.js.map +0 -1
- package/dist/api/Registry.d.ts.map +0 -1
- package/dist/api/Registry.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/loader.d.ts.map +0 -1
- package/dist/loader.js.map +0 -1
- package/dist/runner/AgentRunner.d.ts.map +0 -1
- package/dist/runner/AgentRunner.js.map +0 -1
- package/dist/runner/ModelClient.d.ts.map +0 -1
- package/dist/runner/ModelClient.js.map +0 -1
- package/dist/runner/SwarmRouter.d.ts.map +0 -1
- package/dist/runner/SwarmRouter.js.map +0 -1
- package/dist/runner/ToolDispatcher.d.ts.map +0 -1
- package/dist/runner/ToolDispatcher.js.map +0 -1
- package/dist/session/SessionManager.d.ts.map +0 -1
- package/dist/session/SessionManager.js.map +0 -1
- package/dist/tui/App.d.ts.map +0 -1
- package/dist/tui/App.js.map +0 -1
- package/dist/tui/REPL.d.ts.map +0 -1
- package/dist/tui/REPL.js.map +0 -1
- package/dist/tui/StatusBar.d.ts.map +0 -1
- package/dist/tui/StatusBar.js.map +0 -1
- package/dist/tui/theme.d.ts.map +0 -1
- package/dist/tui/theme.js.map +0 -1
package/dist/tui/App.js
CHANGED
|
@@ -1,20 +1,88 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* App — root Ink component.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Multi-pane TUI with context bar, syntax-highlighted tool output,
|
|
5
|
+
* live side panel with ticker/prices, and command palette triggered via /palette.
|
|
6
6
|
*/
|
|
7
7
|
import { useState, useCallback, useEffect, useRef } from "react";
|
|
8
|
-
import { Box, useApp, useInput } from "ink";
|
|
8
|
+
import { Box, Text, useApp, useInput } from "ink";
|
|
9
9
|
import { StatusBar } from "./StatusBar.js";
|
|
10
10
|
import { REPL } from "./REPL.js";
|
|
11
|
-
import { makeTheme, T } from "./theme.js";
|
|
11
|
+
import { makeTheme, T, JELLY_COLORS } from "./theme.js";
|
|
12
12
|
import { AgentRunner } from "../runner/AgentRunner.js";
|
|
13
13
|
import { SessionManager } from "../session/SessionManager.js";
|
|
14
14
|
import { resolveModelConfig } from "../runner/ModelClient.js";
|
|
15
|
+
import { priceFeed, getPricesTool, topMoversTool, marketOverviewTool, getPricesParams, topMoversParams, marketOverviewParams } from "../tools/PriceFeed.js";
|
|
16
|
+
import { getNewsTool, getNewsParams } from "../tools/NewsSentiment.js";
|
|
17
|
+
import { analyzeTAParams, getCandlesParams, getCandlesTool } from "../tools/TechnicalAnalysis.js";
|
|
18
|
+
import { fullAnalysis } from "../tools/TechnicalAnalysis.js";
|
|
19
|
+
import { newsFeed } from "../tools/NewsSentiment.js";
|
|
20
|
+
import { getFearGreedTool, fearGreedParams, getFundingRatesTool, fundingRatesParams, getBtcMempoolTool, btcMempoolParams, getDefiTvlTool, defiTvlParams, getSolanaStatsTool, solanaStatsParams, } from "../tools/MarketSentiment.js";
|
|
21
|
+
import { contextStore } from "../session/ContextStore.js";
|
|
22
|
+
import { goalManager } from "../session/GoalManager.js";
|
|
23
|
+
import { memoryStore } from "../session/MemoryStore.js";
|
|
24
|
+
import { agentScheduler } from "../scheduler/AgentScheduler.js";
|
|
25
|
+
import { Tracer } from "../telemetry/Tracer.js";
|
|
26
|
+
// ── Context window tracking (#33) ───────────────────────────────────────────
|
|
27
|
+
function getContextBar(session) {
|
|
28
|
+
if (!session)
|
|
29
|
+
return { pct: 0, bar: "░".repeat(20), color: JELLY_COLORS.dim, turboReady: true };
|
|
30
|
+
const pressure = session.getContextPressure();
|
|
31
|
+
const filled = Math.round(pressure.pct / 5);
|
|
32
|
+
const color = pressure.level === "critical" ? JELLY_COLORS.error
|
|
33
|
+
: pressure.level === "red" ? JELLY_COLORS.warn
|
|
34
|
+
: pressure.level === "yellow" ? JELLY_COLORS.header
|
|
35
|
+
: JELLY_COLORS.success;
|
|
36
|
+
return {
|
|
37
|
+
pct: pressure.pct,
|
|
38
|
+
bar: "█".repeat(Math.min(filled, 20)) + "░".repeat(Math.max(0, 20 - filled)),
|
|
39
|
+
color,
|
|
40
|
+
turboReady: pressure.turboReady,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// ── Syntax-highlighted JSON formatter ───────────────────────────────────────
|
|
44
|
+
function highlightJson(obj, indent = 0) {
|
|
45
|
+
const pad = " ".repeat(indent);
|
|
46
|
+
if (obj === null)
|
|
47
|
+
return T.dim("null");
|
|
48
|
+
if (typeof obj === "boolean")
|
|
49
|
+
return T.warn(String(obj));
|
|
50
|
+
if (typeof obj === "number")
|
|
51
|
+
return T.success(String(obj));
|
|
52
|
+
if (typeof obj === "string") {
|
|
53
|
+
if (obj.startsWith("0x") && obj.length > 10)
|
|
54
|
+
return T.header(obj.slice(0, 10) + "…");
|
|
55
|
+
return T.accent(`"${obj}"`);
|
|
56
|
+
}
|
|
57
|
+
if (Array.isArray(obj)) {
|
|
58
|
+
if (obj.length === 0)
|
|
59
|
+
return "[]";
|
|
60
|
+
if (obj.length <= 3)
|
|
61
|
+
return `[${obj.map(v => highlightJson(v, 0)).join(", ")}]`;
|
|
62
|
+
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}]`;
|
|
63
|
+
}
|
|
64
|
+
if (typeof obj === "object") {
|
|
65
|
+
const entries = Object.entries(obj);
|
|
66
|
+
if (entries.length === 0)
|
|
67
|
+
return "{}";
|
|
68
|
+
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}}`;
|
|
69
|
+
}
|
|
70
|
+
return String(obj);
|
|
71
|
+
}
|
|
72
|
+
function formatToolContent(text) {
|
|
73
|
+
try {
|
|
74
|
+
const obj = JSON.parse(text);
|
|
75
|
+
return highlightJson(obj);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return text.length > 300 ? text.slice(0, 300) + T.dim("\n…[truncated]") : text;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
15
81
|
let _msgIdCounter = 0;
|
|
16
82
|
function nextId() { return String(++_msgIdCounter); }
|
|
17
|
-
|
|
83
|
+
// Unique session ID for memory persistence
|
|
84
|
+
const sessionId = `session-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
85
|
+
export function App({ registry, systemPrompt, effectLevel: initialEffect = "normal", chain: initialChain = "ethereum", modelReg, costTracker, onNotifyReady, onStatusReady, }) {
|
|
18
86
|
const { exit } = useApp();
|
|
19
87
|
const [messages, setMessages] = useState([]);
|
|
20
88
|
const [streaming, setStreaming] = useState("");
|
|
@@ -24,16 +92,18 @@ export function App({ registry, systemPrompt, effectLevel: initialEffect = "norm
|
|
|
24
92
|
const [effectLevel, setEffectLevel] = useState(initialEffect);
|
|
25
93
|
const [chain, setChain] = useState(initialChain);
|
|
26
94
|
const [statusBadges, setStatusBadges] = useState({});
|
|
95
|
+
const [ticker, setTicker] = useState("");
|
|
96
|
+
const [costBadge, setCostBadge] = useState("");
|
|
97
|
+
const [newsBadge, setNewsBadge] = useState("");
|
|
27
98
|
const runnerRef = useRef(null);
|
|
28
99
|
const sessionRef = useRef(null);
|
|
29
100
|
const sessionCtxRef = useRef(null);
|
|
30
101
|
const theme = makeTheme();
|
|
31
102
|
let modelName = "no-model";
|
|
32
103
|
try {
|
|
33
|
-
modelName = resolveModelConfig().model;
|
|
104
|
+
modelName = resolveModelConfig(modelReg).model;
|
|
34
105
|
}
|
|
35
106
|
catch { /* shown via banner */ }
|
|
36
|
-
// ── Push helpers ─────────────────────────────────────────────────────────
|
|
37
107
|
const push = useCallback((msg) => {
|
|
38
108
|
setMessages(prev => [...prev, { ...msg, id: nextId(), ts: Date.now() }]);
|
|
39
109
|
}, []);
|
|
@@ -42,70 +112,125 @@ export function App({ registry, systemPrompt, effectLevel: initialEffect = "norm
|
|
|
42
112
|
}, [push]);
|
|
43
113
|
const setStatus = useCallback((key, value) => {
|
|
44
114
|
setStatusBadges(prev => ({ ...prev, [key]: value }));
|
|
45
|
-
|
|
46
|
-
if (key === "vault") {
|
|
115
|
+
if (key === "vault")
|
|
47
116
|
setVaultLocked(!value.includes("🔓") && !value.includes("unlocked"));
|
|
48
|
-
}
|
|
49
117
|
if (key === "chain" || key === "active_chain")
|
|
50
118
|
setChain(value);
|
|
51
119
|
if (key === "effect_level")
|
|
52
120
|
setEffectLevel(value);
|
|
53
121
|
}, []);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
122
|
+
const uiCtx = { notify, setStatus, setTheme(_n) { }, setHeader(_f) { }, theme };
|
|
123
|
+
// Register built-in tools
|
|
124
|
+
function registerBuiltinTools() {
|
|
125
|
+
if (!modelReg)
|
|
126
|
+
return;
|
|
127
|
+
const mk = modelReg;
|
|
128
|
+
registry.addTool({ name: "list_models", label: "List Models", description: "Search and filter available AI models by name, provider, or tier.", parameters: mk.listModelsParams, execute: (id, p) => mk.listModelsTool(id, p) });
|
|
129
|
+
registry.addTool({ name: "pick_model", label: "Pick Model", description: "Find the cheapest available model meeting requirements.", parameters: mk.pickModelParams, execute: (id, p) => mk.pickModelTool(id, p) });
|
|
130
|
+
registry.addTool({ name: "model_summary", label: "Model Summary", description: "Get a summary of available model tiers and counts.", parameters: mk.summaryParams, execute: () => mk.summaryTool() });
|
|
131
|
+
if (costTracker) {
|
|
132
|
+
registry.addTool({ name: "cost_report", label: "Cost Report", description: "Show session and lifetime token usage.", parameters: costTracker.costReportParams, execute: () => costTracker.costReportTool() });
|
|
133
|
+
}
|
|
134
|
+
registry.addTool({ name: "get_prices", label: "Get Prices", description: "Get current prices and 24h change.", parameters: getPricesParams, execute: (id, p) => getPricesTool(id, p) });
|
|
135
|
+
registry.addTool({ name: "get_top_movers", label: "Top Movers", description: "Assets with largest 24h price movement.", parameters: topMoversParams, execute: (id, p) => topMoversTool(id, p) });
|
|
136
|
+
registry.addTool({ name: "get_market_overview", label: "Market Overview", description: "Aggregated market data.", parameters: marketOverviewParams, execute: () => marketOverviewTool() });
|
|
137
|
+
registry.addTool({ name: "get_news", label: "Get News", description: "Crypto news headlines with sentiment scoring.", parameters: getNewsParams, execute: (id, p) => getNewsTool(id, p) });
|
|
138
|
+
// #18: OHLCV candles from Binance + TA analysis
|
|
139
|
+
registry.addTool({ name: "get_candles", label: "Get Candles", description: "Fetch OHLCV candlestick data from Binance and run technical analysis (RSI, MACD, Bollinger, EMA). Use this before analyze_ta.", parameters: getCandlesParams, execute: (id, p) => getCandlesTool(id, p) });
|
|
140
|
+
// #20: Free market sentiment tools
|
|
141
|
+
registry.addTool({ name: "get_fear_greed", label: "Fear & Greed", description: "Crypto Fear & Greed Index with 7-day history.", parameters: fearGreedParams, execute: (id, p) => getFearGreedTool(id, p) });
|
|
142
|
+
registry.addTool({ name: "get_funding_rates", label: "Funding Rates", description: "Binance perpetual funding rates — shows long/short bias.", parameters: fundingRatesParams, execute: (id, p) => getFundingRatesTool(id, p) });
|
|
143
|
+
registry.addTool({ name: "get_btc_mempool", label: "BTC Mempool", description: "Bitcoin mempool stats and recommended fee rates.", parameters: btcMempoolParams, execute: () => getBtcMempoolTool() });
|
|
144
|
+
registry.addTool({ name: "get_defi_tvl", label: "DeFi TVL", description: "DeFiLlama TVL by chain or all chains.", parameters: defiTvlParams, execute: (id, p) => getDefiTvlTool(id, p) });
|
|
145
|
+
registry.addTool({ name: "get_solana_stats", label: "Solana Stats", description: "Solana network TPS and health.", parameters: solanaStatsParams, execute: () => getSolanaStatsTool() });
|
|
146
|
+
// #31: Ephemeral task context
|
|
147
|
+
registry.addTool({ name: "read_task_context", label: "Read Task Context", description: "Read saved task context from a previous multi-step operation.", parameters: require("@sinclair/typebox").Type.Object({ taskId: require("@sinclair/typebox").Type.String({ description: "Task ID from a previous task reference" }) }), execute: (id, p) => contextStore.readContextTool(id, p) });
|
|
148
|
+
registry.addTool({ name: "list_tasks", label: "List Tasks", description: "List active and recent task context folders.", parameters: require("@sinclair/typebox").Type.Object({}), execute: () => contextStore.listTasksTool() });
|
|
149
|
+
// #12: Goal management
|
|
150
|
+
registry.addTool({ name: "set_goal", label: "Set Goal", description: "Set a persistent cross-session goal for the agent to monitor.", parameters: goalManager.setGoalParams, execute: (id, p) => goalManager.setGoalTool(id, p) });
|
|
151
|
+
registry.addTool({ name: "complete_goal", label: "Complete Goal", description: "Mark a goal as completed.", parameters: goalManager.completeGoalParams, execute: (id, p) => goalManager.completeGoalTool(id, p) });
|
|
152
|
+
registry.addTool({ name: "list_goals", label: "List Goals", description: "List active or all persistent goals.", parameters: goalManager.listGoalsParams, execute: (id, p) => goalManager.listGoalsTool(id, p) });
|
|
153
|
+
registry.addTool({ name: "add_goal_note", label: "Add Goal Note", description: "Add a progress note to an existing goal.", parameters: goalManager.addGoalNoteParams, execute: (id, p) => goalManager.addGoalNoteTool(id, p) });
|
|
154
|
+
registry.addTool({ name: "analyze_ta", label: "Technical Analysis", description: "RSI, MACD, Bollinger, EMA crossover, ATR. Tip: use get_candles first to fetch real price data.", parameters: analyzeTAParams, execute: async (_id, p) => {
|
|
155
|
+
const closes = p.prices;
|
|
156
|
+
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 }));
|
|
157
|
+
const results = fullAnalysis(candles);
|
|
158
|
+
const summary = results.find((r) => r.indicator === "SUMMARY");
|
|
159
|
+
const text = results.map((r) => {
|
|
160
|
+
const s = r.signal === "bullish" ? "🟢" : r.signal === "bearish" ? "🔴" : "⚪";
|
|
161
|
+
const v = Array.isArray(r.value) ? `[${r.value.length}]` : typeof r.value === "number" ? r.value : "-";
|
|
162
|
+
return `${s} ${r.indicator}: ${v}`;
|
|
163
|
+
}).join("\n");
|
|
164
|
+
return { content: [{ type: "text", text: `Technical Analysis:\n${text}` }], details: { results, overall_signal: summary?.signal, overall_score: summary?.value } };
|
|
165
|
+
} });
|
|
166
|
+
}
|
|
167
|
+
useEffect(() => { onNotifyReady?.(notify); onStatusReady?.(setStatus); }, []);
|
|
68
168
|
useEffect(() => {
|
|
169
|
+
registerBuiltinTools();
|
|
170
|
+
priceFeed.track("btc", "eth", "sol", "bnb", "matic", "arb", "op", "avax", "link", "uni", "doge", "xrp", "ada", "dot", "atom", "near", "sui", "apt", "pepe", "aave");
|
|
171
|
+
priceFeed.start();
|
|
172
|
+
newsFeed.start();
|
|
173
|
+
// #11: Register scheduler tools
|
|
174
|
+
registry.addTool({ name: "schedule_task", label: "Schedule Task", description: "Schedule a recurring or one-shot agent task (cron or price trigger).", parameters: agentScheduler.addTaskParams, execute: (id, p) => agentScheduler.addTaskTool(id, p) });
|
|
175
|
+
registry.addTool({ name: "list_schedule", label: "List Schedule", description: "List all scheduled tasks.", parameters: agentScheduler.listTasksParams, execute: (id, p) => agentScheduler.listTasksTool(id, p) });
|
|
176
|
+
registry.addTool({ name: "remove_schedule", label: "Remove Schedule", description: "Remove a scheduled task by ID.", parameters: agentScheduler.removeTaskParams, execute: (id, p) => agentScheduler.removeTaskTool(id, p) });
|
|
177
|
+
// Start scheduler — fires agent turns autonomously
|
|
178
|
+
agentScheduler.start((task) => {
|
|
179
|
+
push({ role: "system", content: T.muted(`⏰ Scheduler: running "${task.name}"`) });
|
|
180
|
+
// Only fire if agent is not currently busy
|
|
181
|
+
if (!disabled && runnerRef.current) {
|
|
182
|
+
setDisabled(true);
|
|
183
|
+
setStreaming("");
|
|
184
|
+
runnerRef.current.run(`[SCHEDULED TASK: ${task.name}] ${task.prompt}`)
|
|
185
|
+
.catch((e) => notify(T.error(`Scheduler error: ${e.message}`)));
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
const tickerInterval = setInterval(() => {
|
|
189
|
+
setTicker(priceFeed.tickerLine(6));
|
|
190
|
+
if (costTracker)
|
|
191
|
+
setCostBadge(costTracker.statusLine());
|
|
192
|
+
setNewsBadge(newsFeed.statusBadge());
|
|
193
|
+
}, 5_000);
|
|
194
|
+
const saveInterval = setInterval(() => { costTracker?.saveLifetime(); }, 60_000);
|
|
69
195
|
const session = new SessionManager();
|
|
70
|
-
session.setSystemPrompt(registry.getSystemPrompt() || systemPrompt ||
|
|
71
|
-
"You are JellyOS, an autonomous AI trading agent.");
|
|
196
|
+
session.setSystemPrompt(registry.getSystemPrompt() || systemPrompt || "You are JellyOS, an autonomous AI trading agent.");
|
|
72
197
|
sessionRef.current = session;
|
|
73
|
-
const sessionCtx = {
|
|
74
|
-
ui: uiCtx,
|
|
75
|
-
hasUI: true,
|
|
76
|
-
config: {
|
|
77
|
-
OPENROUTER_API_KEY: process.env.OPENROUTER_API_KEY,
|
|
78
|
-
ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY,
|
|
79
|
-
ALCHEMY_KEY: process.env.ALCHEMY_KEY,
|
|
80
|
-
DEFAULT_MODEL: process.env.DEFAULT_MODEL,
|
|
81
|
-
},
|
|
82
|
-
};
|
|
198
|
+
const sessionCtx = { ui: uiCtx, hasUI: true, config: { OPENROUTER_API_KEY: process.env.OPENROUTER_API_KEY, ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY, ALCHEMY_KEY: process.env.ALCHEMY_KEY, DEFAULT_MODEL: process.env.DEFAULT_MODEL } };
|
|
83
199
|
sessionCtxRef.current = sessionCtx;
|
|
84
200
|
registry.fireHook("session_start", sessionCtx).then(() => {
|
|
85
|
-
|
|
201
|
+
// #7: Inject memory context into system prompt
|
|
202
|
+
if (memoryStore.isAvailable) {
|
|
203
|
+
const memCtx = memoryStore.buildContextBlock(sessionId);
|
|
204
|
+
if (memCtx) {
|
|
205
|
+
session.setSystemPrompt((session.getSystemPrompt() || "") + memCtx);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// #12: Inject active goals into system prompt
|
|
209
|
+
const goalCtx = goalManager.buildContextBlock();
|
|
210
|
+
if (goalCtx) {
|
|
211
|
+
session.setSystemPrompt((session.getSystemPrompt() || "") + goalCtx);
|
|
212
|
+
}
|
|
213
|
+
push({ role: "system", content: T.muted("Session started. Type /help for commands.") });
|
|
86
214
|
});
|
|
87
215
|
const runner = new AgentRunner(registry, session, (event) => {
|
|
88
|
-
if (event.type === "text_delta")
|
|
216
|
+
if (event.type === "text_delta")
|
|
89
217
|
setStreaming(prev => prev + event.text);
|
|
90
|
-
|
|
91
|
-
else if (event.type === "tool_start") {
|
|
218
|
+
else if (event.type === "tool_start")
|
|
92
219
|
setToolRunning(event.name);
|
|
93
|
-
}
|
|
94
220
|
else if (event.type === "tool_done") {
|
|
95
221
|
setToolRunning(null);
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
toolName: event.name,
|
|
100
|
-
isError: event.isError,
|
|
101
|
-
});
|
|
222
|
+
// Format tool output with syntax highlighting
|
|
223
|
+
const formatted = formatToolContent(event.result);
|
|
224
|
+
push({ role: "tool", content: formatted, toolName: event.name, isError: event.isError });
|
|
102
225
|
}
|
|
103
226
|
else if (event.type === "turn_done") {
|
|
104
227
|
setDisabled(false);
|
|
105
228
|
setToolRunning(null);
|
|
106
229
|
setStreaming(prev => {
|
|
107
|
-
if (prev.trim())
|
|
230
|
+
if (prev.trim()) {
|
|
108
231
|
push({ role: "assistant", content: prev });
|
|
232
|
+
memoryStore.save(sessionId, "assistant", prev); // #7 persist to memory
|
|
233
|
+
}
|
|
109
234
|
return "";
|
|
110
235
|
});
|
|
111
236
|
}
|
|
@@ -115,23 +240,47 @@ export function App({ registry, systemPrompt, effectLevel: initialEffect = "norm
|
|
|
115
240
|
setStreaming("");
|
|
116
241
|
notify(T.error(`Error: ${event.message}`));
|
|
117
242
|
}
|
|
118
|
-
|
|
243
|
+
else if (event.type === "approval_request") {
|
|
244
|
+
// #10: Show approval prompt — user must type y or n
|
|
245
|
+
setToolRunning(null);
|
|
246
|
+
const { toolName, args, approve } = event;
|
|
247
|
+
let parsed = "";
|
|
248
|
+
try {
|
|
249
|
+
parsed = JSON.stringify(JSON.parse(args), null, 2).slice(0, 200);
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
parsed = args.slice(0, 200);
|
|
253
|
+
}
|
|
254
|
+
push({
|
|
255
|
+
role: "notify",
|
|
256
|
+
content: `⚠️ APPROVAL REQUIRED\n\nTool: ${toolName}\nArgs: ${parsed}\n\nType /approve or /deny to continue.`,
|
|
257
|
+
});
|
|
258
|
+
// Store the approve callback so /approve /deny commands can resolve it
|
|
259
|
+
runnerRef.current._pendingApproval = approve;
|
|
260
|
+
}
|
|
261
|
+
}, sessionCtx, effectLevel, modelReg, costTracker, goalManager, contextStore);
|
|
119
262
|
runnerRef.current = runner;
|
|
263
|
+
setTicker(priceFeed.tickerLine(6));
|
|
264
|
+
if (costTracker)
|
|
265
|
+
setCostBadge(costTracker.statusLine());
|
|
266
|
+
setNewsBadge(newsFeed.statusBadge());
|
|
120
267
|
return () => {
|
|
121
|
-
if (sessionCtxRef.current)
|
|
268
|
+
if (sessionCtxRef.current)
|
|
122
269
|
registry.fireHook("session_end", sessionCtxRef.current).catch(() => { });
|
|
123
|
-
|
|
270
|
+
priceFeed.stop();
|
|
271
|
+
newsFeed.stop();
|
|
272
|
+
agentScheduler.stop();
|
|
273
|
+
clearInterval(tickerInterval);
|
|
274
|
+
clearInterval(saveInterval);
|
|
275
|
+
costTracker?.saveLifetime();
|
|
124
276
|
};
|
|
125
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
126
277
|
}, []);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
runnerRef.current?.setEffectLevel(effectLevel);
|
|
130
|
-
}, [effectLevel]);
|
|
131
|
-
// ── Ctrl-C ────────────────────────────────────────────────────────────────
|
|
278
|
+
useEffect(() => { runnerRef.current?.setEffectLevel(effectLevel); }, [effectLevel]);
|
|
279
|
+
// ── Ctrl-C to exit ──────────────────────────────────────────────────────
|
|
132
280
|
useInput((_input, key) => {
|
|
133
281
|
if (key.ctrl && _input === "c") {
|
|
134
282
|
push({ role: "system", content: T.muted("Goodbye 🪼") });
|
|
283
|
+
costTracker?.saveLifetime();
|
|
135
284
|
setTimeout(exit, 200);
|
|
136
285
|
}
|
|
137
286
|
});
|
|
@@ -145,24 +294,147 @@ export function App({ registry, systemPrompt, effectLevel: initialEffect = "norm
|
|
|
145
294
|
const args = rest.join(" ");
|
|
146
295
|
if (cmd === "exit" || cmd === "quit") {
|
|
147
296
|
push({ role: "system", content: T.muted("Goodbye 🪼") });
|
|
297
|
+
costTracker?.saveLifetime();
|
|
148
298
|
setTimeout(exit, 200);
|
|
149
299
|
return;
|
|
150
300
|
}
|
|
151
301
|
if (cmd === "help") {
|
|
152
302
|
const lines = registry.listCommands().map(([n, d]) => T.accent(`/${n}`.padEnd(16)) + " " + d.description);
|
|
153
|
-
notify("
|
|
303
|
+
notify("Commands:\n" + lines.join("\n") + "\n\nTools: list_models, pick_model, get_prices, get_news, analyze_ta, cost_report, get_market_overview, get_top_movers, model_summary");
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (cmd === "models") {
|
|
307
|
+
if (!modelReg) {
|
|
308
|
+
notify(T.error("Model registry not available"));
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const result = await modelReg.listModelsTool("", { query: rest.join(" ") || undefined, limit: 20, available_only: true });
|
|
312
|
+
notify(result.content[0].text);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
if (cmd === "cost") {
|
|
316
|
+
if (!costTracker) {
|
|
317
|
+
notify(T.error("Cost tracker not available"));
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const result = await costTracker.costReportTool();
|
|
321
|
+
notify(result.content[0].text);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
if (cmd === "news") {
|
|
325
|
+
const result = await getNewsTool("", { limit: 15 });
|
|
326
|
+
notify(result.content[0].text);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (cmd === "prices") {
|
|
330
|
+
const result = await getPricesTool("", { symbols: args ? args.split(/\s+/) : ["btc", "eth", "sol"] });
|
|
331
|
+
notify(result.content[0].text);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
if (cmd === "palette") {
|
|
335
|
+
// Command palette — list all commands and tools
|
|
336
|
+
const lines = registry.listCommands().map(([n, d]) => T.accent(`/${n}`.padEnd(16)) + " " + d.description);
|
|
337
|
+
const tools = registry.listTools().map(t => T.success(`🔧 ${t.name.padEnd(22)}`) + " " + T.muted(t.description.slice(0, 50)));
|
|
338
|
+
notify("Command Palette\n\n" + lines.join("\n") + "\n\nTools:\n" + tools.join("\n"));
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (cmd === "effect" && ["eco", "normal", "turbo", "max"].includes(args.trim().toLowerCase())) {
|
|
342
|
+
setEffectLevel(args.trim().toLowerCase());
|
|
343
|
+
notify(T.accent(`Effect → ${args.trim().toUpperCase()}`));
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
// #12: Goal commands
|
|
347
|
+
if (cmd === "goals" || cmd === "goal") {
|
|
348
|
+
const subCmd = args.trim().split(" ")[0];
|
|
349
|
+
if (subCmd === "add") {
|
|
350
|
+
const text = args.trim().slice(4).trim();
|
|
351
|
+
if (!text) {
|
|
352
|
+
notify(T.error("Usage: /goal add <text>"));
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
const result = await goalManager.setGoalTool("", { text });
|
|
356
|
+
notify(result.content[0].text);
|
|
357
|
+
}
|
|
358
|
+
else if (subCmd === "done" || subCmd === "complete") {
|
|
359
|
+
const id = args.trim().split(" ")[1];
|
|
360
|
+
if (!id) {
|
|
361
|
+
notify(T.error("Usage: /goal done <id>"));
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
const result = await goalManager.completeGoalTool("", { id });
|
|
365
|
+
notify(result.content[0].text);
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
const result = await goalManager.listGoalsTool("", { status: "all" });
|
|
369
|
+
notify(result.content[0].text);
|
|
370
|
+
}
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
// #31: Task context commands
|
|
374
|
+
if (cmd === "tasks") {
|
|
375
|
+
const result = await contextStore.listTasksTool();
|
|
376
|
+
notify(result.content[0].text);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
if (cmd === "keep" && args.trim()) {
|
|
380
|
+
const ok = contextStore.keepTask(args.trim());
|
|
381
|
+
notify(ok ? T.success(`Task ${args.trim()} marked for permanent retention.`) : T.error(`Task ${args.trim()} not found.`));
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
// #10: Approval gate responses
|
|
385
|
+
if (cmd === "approve" || cmd === "y") {
|
|
386
|
+
const pending = runnerRef.current?._pendingApproval;
|
|
387
|
+
if (pending) {
|
|
388
|
+
pending(true);
|
|
389
|
+
push({ role: "system", content: T.success("✅ Tool approved.") });
|
|
390
|
+
}
|
|
391
|
+
else
|
|
392
|
+
notify(T.error("No pending approval."));
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
if (cmd === "deny" || cmd === "n") {
|
|
396
|
+
const pending = runnerRef.current?._pendingApproval;
|
|
397
|
+
if (pending) {
|
|
398
|
+
pending(false);
|
|
399
|
+
push({ role: "system", content: T.error("❌ Tool denied.") });
|
|
400
|
+
}
|
|
401
|
+
else
|
|
402
|
+
notify(T.error("No pending approval."));
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
// #30: Traces command
|
|
406
|
+
if (cmd === "traces") {
|
|
407
|
+
const recent = Tracer.readRecent(5);
|
|
408
|
+
notify(Tracer.formatSummary(recent));
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
// #11: Scheduler commands
|
|
412
|
+
if (cmd === "schedule" || cmd === "sched") {
|
|
413
|
+
const result = await agentScheduler.listTasksTool("", { enabled_only: false });
|
|
414
|
+
notify(result.content[0].text);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
// #7: Memory search
|
|
418
|
+
if (cmd === "memory" && args.trim()) {
|
|
419
|
+
const results = memoryStore.search(args.trim(), 8);
|
|
420
|
+
if (results.length === 0) {
|
|
421
|
+
notify(T.muted("No memories found for: " + args.trim()));
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
const lines = results.map(r => `[${new Date(r.ts).toLocaleDateString()} ${r.role}] ${r.content.slice(0, 120)}`);
|
|
425
|
+
notify(`Memory search: "${args.trim()}"\n${lines.join("\n")}`);
|
|
154
426
|
return;
|
|
155
427
|
}
|
|
156
428
|
const def = registry.getCommand(cmd);
|
|
157
429
|
if (!def) {
|
|
158
|
-
notify(T.error(`Unknown
|
|
430
|
+
notify(T.error(`Unknown: /${cmd} — try /help or /palette`));
|
|
159
431
|
return;
|
|
160
432
|
}
|
|
161
433
|
try {
|
|
162
434
|
await def.handler(args, { ui: uiCtx });
|
|
163
435
|
}
|
|
164
436
|
catch (e) {
|
|
165
|
-
notify(T.error(`
|
|
437
|
+
notify(T.error(`Error: ${e.message}`));
|
|
166
438
|
}
|
|
167
439
|
return;
|
|
168
440
|
}
|
|
@@ -171,6 +443,7 @@ export function App({ registry, systemPrompt, effectLevel: initialEffect = "norm
|
|
|
171
443
|
return;
|
|
172
444
|
}
|
|
173
445
|
push({ role: "user", content: input });
|
|
446
|
+
memoryStore.save(sessionId, "user", input); // #7 persist user message
|
|
174
447
|
setDisabled(true);
|
|
175
448
|
setStreaming("");
|
|
176
449
|
try {
|
|
@@ -178,11 +451,23 @@ export function App({ registry, systemPrompt, effectLevel: initialEffect = "norm
|
|
|
178
451
|
}
|
|
179
452
|
catch (e) {
|
|
180
453
|
setDisabled(false);
|
|
181
|
-
notify(T.error(`
|
|
454
|
+
notify(T.error(`Error: ${e.message}`));
|
|
182
455
|
}
|
|
183
|
-
}, [registry, exit, push, notify, uiCtx]);
|
|
184
|
-
|
|
185
|
-
const statusLine = Object.values(statusBadges).join(" ") || null;
|
|
186
|
-
|
|
456
|
+
}, [registry, exit, push, notify, uiCtx, modelReg, costTracker]);
|
|
457
|
+
const ctx = getContextBar(sessionRef.current);
|
|
458
|
+
const statusLine = [ticker, costBadge, newsBadge, ...Object.values(statusBadges)].filter(Boolean).join(" ") || null;
|
|
459
|
+
// ── Multi-pane layout ────────────────────────────────────────────────────
|
|
460
|
+
return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [_jsx(StatusBar, { model: modelName, chain: chain, vaultLocked: vaultLocked, effectLevel: effectLevel, toolRunning: toolRunning, connected: true, statusLine: statusLine }), _jsxs(Box, { flexDirection: "row", flexGrow: 1, overflow: "hidden", children: [_jsxs(Box, { flexDirection: "column", width: 32, borderStyle: "single", borderColor: JELLY_COLORS.dim, paddingX: 1, flexShrink: 0, children: [_jsx(Text, { color: JELLY_COLORS.accent, bold: true, children: "\uD83D\uDCE1 Ticker" }), _jsx(Text, { color: JELLY_COLORS.muted, wrap: "truncate", children: ticker || "Loading…" }), _jsx(Text, { color: JELLY_COLORS.dim, children: "─".repeat(28) }), _jsxs(Text, { color: JELLY_COLORS.accent, children: ["Context ", ctx.turboReady ? "" : "⚠"] }), _jsxs(Text, { color: ctx.color, children: [ctx.bar, " ", ctx.pct, "%", ctx.turboReady ? "" : " no turbo"] }), _jsx(Text, { color: JELLY_COLORS.dim, children: "─".repeat(28) }), _jsx(Text, { color: JELLY_COLORS.accent, children: "Effect" }), _jsx(Text, { color: effectLevel === "eco" ? "#22c55e" : effectLevel === "turbo" ? "#f59e0b" : effectLevel === "max" ? "#ef4444" : JELLY_COLORS.accent, children: effectLevel.toUpperCase() }), _jsx(Text, { color: JELLY_COLORS.dim, children: "─".repeat(28) }), _jsx(Text, { color: JELLY_COLORS.accent, children: "News" }), _jsx(Text, { color: JELLY_COLORS.muted, wrap: "truncate", children: newsBadge || "…" })] }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: _jsx(REPL, { messages: messages, streamingText: streaming, toolRunning: toolRunning, onSubmit: handleSubmit, disabled: disabled, onAbort: () => {
|
|
461
|
+
// #25: Escape aborts in-flight stream
|
|
462
|
+
runnerRef.current?.abort();
|
|
463
|
+
setDisabled(false);
|
|
464
|
+
setToolRunning(null);
|
|
465
|
+
setStreaming(prev => {
|
|
466
|
+
if (prev.trim())
|
|
467
|
+
push({ role: "assistant", content: prev + " \u2014 [interrupted]" });
|
|
468
|
+
return "";
|
|
469
|
+
});
|
|
470
|
+
push({ role: "system", content: T.dim("— stream interrupted —") });
|
|
471
|
+
} }) })] })] }));
|
|
187
472
|
}
|
|
188
473
|
//# sourceMappingURL=App.js.map
|
package/dist/tui/REPL.d.ts
CHANGED
|
@@ -16,7 +16,8 @@ export interface REPLProps {
|
|
|
16
16
|
streamingText: string;
|
|
17
17
|
toolRunning: string | null;
|
|
18
18
|
onSubmit(input: string): void;
|
|
19
|
+
onAbort?(): void;
|
|
19
20
|
disabled: boolean;
|
|
20
21
|
}
|
|
21
|
-
export declare function REPL({ messages, streamingText, toolRunning, onSubmit, disabled }: REPLProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export declare function REPL({ messages, streamingText, toolRunning, onSubmit, onAbort, disabled }: REPLProps): import("react/jsx-runtime").JSX.Element;
|
|
22
23
|
//# sourceMappingURL=REPL.d.ts.map
|
package/dist/tui/REPL.js
CHANGED
|
@@ -4,7 +4,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
* Renders assistant text, tool calls, tool results, and user messages.
|
|
5
5
|
*/
|
|
6
6
|
import { useState, useCallback } from "react";
|
|
7
|
-
import { Box, Text, useStdout } from "ink";
|
|
7
|
+
import { Box, Text, useStdout, useInput } from "ink";
|
|
8
8
|
import TextInput from "ink-text-input";
|
|
9
9
|
import { JELLY_COLORS } from "./theme.js";
|
|
10
10
|
const MAX_VISIBLE = 40;
|
|
@@ -19,9 +19,8 @@ function MessageLine({ msg }) {
|
|
|
19
19
|
if (msg.role === "tool") {
|
|
20
20
|
const icon = msg.isError ? "✗" : "✓";
|
|
21
21
|
const col = msg.isError ? JELLY_COLORS.error : JELLY_COLORS.success;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
: _jsxs(Text, { color: JELLY_COLORS.muted, children: [" (", msg.content.length, " chars)"] })] }));
|
|
22
|
+
const isLong = msg.content.length > 120;
|
|
23
|
+
return (_jsxs(Box, { flexDirection: "column", marginY: 0, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: JELLY_COLORS.muted, children: time }), _jsxs(Text, { color: col, children: [icon, " ", msg.toolName ?? "tool"] }), isLong && _jsxs(Text, { color: JELLY_COLORS.dim, children: [" (", msg.content.length, " chars)"] })] }), !isLong && (_jsx(Box, { marginLeft: 6, children: _jsx(Text, { color: JELLY_COLORS.muted, wrap: "wrap", children: msg.content }) }))] }));
|
|
25
24
|
}
|
|
26
25
|
if (msg.role === "notify") {
|
|
27
26
|
return (_jsx(Box, { borderStyle: "round", borderColor: JELLY_COLORS.accent, marginY: 1, paddingX: 1, children: _jsx(Text, { wrap: "wrap", children: msg.content }) }));
|
|
@@ -29,7 +28,7 @@ function MessageLine({ msg }) {
|
|
|
29
28
|
// system messages — dimmed
|
|
30
29
|
return (_jsx(Box, { flexDirection: "row", gap: 1, children: _jsx(Text, { color: JELLY_COLORS.dim, wrap: "wrap", children: msg.content }) }));
|
|
31
30
|
}
|
|
32
|
-
export function REPL({ messages, streamingText, toolRunning, onSubmit, disabled }) {
|
|
31
|
+
export function REPL({ messages, streamingText, toolRunning, onSubmit, onAbort, disabled }) {
|
|
33
32
|
const [input, setInput] = useState("");
|
|
34
33
|
const { stdout } = useStdout();
|
|
35
34
|
const termWidth = stdout?.columns ?? 80;
|
|
@@ -40,7 +39,13 @@ export function REPL({ messages, streamingText, toolRunning, onSubmit, disabled
|
|
|
40
39
|
setInput("");
|
|
41
40
|
onSubmit(trimmed);
|
|
42
41
|
}, [onSubmit, disabled]);
|
|
42
|
+
// #25: Escape key aborts in-flight stream
|
|
43
|
+
useInput((_input, key) => {
|
|
44
|
+
if (key.escape && disabled && onAbort) {
|
|
45
|
+
onAbort();
|
|
46
|
+
}
|
|
47
|
+
});
|
|
43
48
|
const visible = messages.slice(-MAX_VISIBLE);
|
|
44
|
-
return (_jsxs(Box, { flexDirection: "column", width: termWidth, children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [visible.map(m => _jsx(MessageLine, { msg: m }, m.id)), streamingText && (_jsxs(Box, { flexDirection: "row", gap: 1, marginTop: 1, children: [_jsx(Text, { color: JELLY_COLORS.muted, children: " " }), _jsx(Text, { color: JELLY_COLORS.header, bold: true, children: "\uD83E\uDEBC " }), _jsx(Text, { wrap: "wrap", children: streamingText })] })), toolRunning && (_jsx(Box, { flexDirection: "row", gap: 1, marginTop: 1, children: _jsxs(Text, { color: JELLY_COLORS.warn, children: ["\u2699 running ", toolRunning, "\u2026"] }) }))] }), _jsxs(Box, { borderStyle: "round", borderColor: disabled ? JELLY_COLORS.dim : JELLY_COLORS.accent, paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: JELLY_COLORS.accent, children: "\u203A " }), _jsx(TextInput, { value: input, onChange: setInput, onSubmit: handleSubmit, placeholder: disabled ? "thinking…" : "message or /command" })] }), _jsx(Box, { paddingX: 2, children:
|
|
49
|
+
return (_jsxs(Box, { flexDirection: "column", width: termWidth, children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [visible.map(m => _jsx(MessageLine, { msg: m }, m.id)), streamingText && (_jsxs(Box, { flexDirection: "row", gap: 1, marginTop: 1, children: [_jsx(Text, { color: JELLY_COLORS.muted, children: " " }), _jsx(Text, { color: JELLY_COLORS.header, bold: true, children: "\uD83E\uDEBC " }), _jsx(Text, { wrap: "wrap", children: streamingText })] })), toolRunning && (_jsx(Box, { flexDirection: "row", gap: 1, marginTop: 1, children: _jsxs(Text, { color: JELLY_COLORS.warn, children: ["\u2699 running ", toolRunning, "\u2026"] }) }))] }), _jsxs(Box, { borderStyle: "round", borderColor: disabled ? JELLY_COLORS.dim : JELLY_COLORS.accent, paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: JELLY_COLORS.accent, children: "\u203A " }), _jsx(TextInput, { value: input, onChange: setInput, onSubmit: handleSubmit, placeholder: disabled ? "thinking…" : "message or /command" })] }), _jsx(Box, { paddingX: 2, children: _jsxs(Text, { color: JELLY_COLORS.dim, children: ["/help \u00B7 /palette \u00B7 /cost \u00B7 /prices \u00B7 /news \u00B7 /goals \u00B7 /tasks \u00B7 ", disabled ? "Esc=abort · " : "", "Ctrl-C to exit"] }) })] }));
|
|
45
50
|
}
|
|
46
51
|
//# sourceMappingURL=REPL.js.map
|
package/dist/tui/StatusBar.js
CHANGED
|
@@ -9,7 +9,7 @@ export function StatusBar({ model, chain, vaultLocked, effectLevel, toolRunning,
|
|
|
9
9
|
return (_jsxs(Box, { borderStyle: "single", borderColor: JELLY_COLORS.dim, paddingX: 1, flexDirection: "row", justifyContent: "space-between", children: [_jsxs(Box, { gap: 2, children: [_jsx(Text, { color: JELLY_COLORS.accent, bold: true, children: "\uD83E\uDEBC JellyOS" }), _jsx(Text, { color: JELLY_COLORS.muted, children: modelShort })] }), _jsx(Box, { children: toolRunning
|
|
10
10
|
? _jsxs(Text, { color: JELLY_COLORS.warn, children: ["\u2699 ", toolRunning] })
|
|
11
11
|
: statusLine
|
|
12
|
-
? _jsx(Text, { color: JELLY_COLORS.
|
|
12
|
+
? _jsx(Text, { color: JELLY_COLORS.muted, children: statusLine.slice(0, 80) })
|
|
13
13
|
: _jsx(Text, { color: JELLY_COLORS.muted, children: connected ? "ready" : "connecting…" }) }), _jsxs(Box, { gap: 2, children: [_jsx(Text, { color: JELLY_COLORS.muted, children: chainShort }), _jsx(Text, { children: vaultIcon }), _jsxs(Text, { color: JELLY_COLORS.header, children: [effectIcon, " ", effectLevel] })] })] }));
|
|
14
14
|
}
|
|
15
15
|
//# sourceMappingURL=StatusBar.js.map
|