@jellyos/agent 0.1.4 → 0.1.6
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.npm.md +212 -0
- package/bin/jellyos-mcp +26 -0
- package/dist/api/ExtensionAPI.d.ts +11 -0
- package/dist/cli.js +127 -49
- package/dist/index.d.ts +15 -2
- package/dist/index.js +13 -3
- package/dist/loader.d.ts +2 -9
- package/dist/loader.js +2 -1
- 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/ModelRegistry.d.ts +12 -1
- package/dist/models/ModelRegistry.js +105 -9
- package/dist/runner/AgentRunner.d.ts +19 -2
- package/dist/runner/AgentRunner.js +247 -17
- package/dist/runner/ModelClient.d.ts +10 -1
- package/dist/runner/ModelClient.js +79 -6
- package/dist/runner/SwarmRouter.d.ts +6 -6
- package/dist/runner/SwarmRouter.js +73 -24
- 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/tools/MarketSentiment.d.ts +166 -0
- package/dist/tools/MarketSentiment.js +209 -0
- package/dist/tools/NewsSentiment.js +40 -13
- package/dist/tools/PriceFeed.d.ts +2 -0
- package/dist/tools/PriceFeed.js +79 -27
- package/dist/tools/TechnicalAnalysis.d.ts +37 -0
- package/dist/tools/TechnicalAnalysis.js +85 -0
- package/dist/tui/App.d.ts +4 -3
- package/dist/tui/App.js +346 -119
- package/dist/tui/ModelSelector.d.ts +22 -0
- package/dist/tui/ModelSelector.js +86 -0
- package/dist/tui/REPL.d.ts +2 -1
- package/dist/tui/REPL.js +11 -6
- package/package.json +10 -6
- 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/models/CostTracker.d.ts.map +0 -1
- package/dist/models/CostTracker.js.map +0 -1
- package/dist/models/ModelRegistry.d.ts.map +0 -1
- package/dist/models/ModelRegistry.js.map +0 -1
- package/dist/models/index.d.ts.map +0 -1
- package/dist/models/index.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/tools/NewsSentiment.d.ts.map +0 -1
- package/dist/tools/NewsSentiment.js.map +0 -1
- package/dist/tools/PriceFeed.d.ts.map +0 -1
- package/dist/tools/PriceFeed.js.map +0 -1
- package/dist/tools/TechnicalAnalysis.d.ts.map +0 -1
- package/dist/tools/TechnicalAnalysis.js.map +0 -1
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.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/README.npm.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# @jellyos/agent
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
<pre>
|
|
6
|
+
██╗███████╗██╗ ██╗ ██╗ ██╗ ██████╗ ███████╗
|
|
7
|
+
██║██╔════╝██║ ██║ ╚██╗ ██╔╝ ██╔═══██╗██╔════╝
|
|
8
|
+
██║█████╗ ██║ ██║ ╚████╔╝ ██║ ██║███████╗
|
|
9
|
+
██ ██║██╔══╝ ██║ ██║ ╚██╔╝ ██║ ██║╚════██║
|
|
10
|
+
╚█████╔╝███████╗███████╗██║ ██║ ╚██████╔╝███████║
|
|
11
|
+
╚════╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝
|
|
12
|
+
</pre>
|
|
13
|
+
|
|
14
|
+
**Autonomous AI trading agent. Runs 100% locally. No server. No inbound ports.**
|
|
15
|
+
|
|
16
|
+
[](https://www.npmjs.com/package/@jellyos/agent)
|
|
17
|
+
[](https://nodejs.org)
|
|
18
|
+
[](LICENSE)
|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## What's in this package
|
|
25
|
+
|
|
26
|
+
This NPM package contains the **core JellyOS agent engine**:
|
|
27
|
+
|
|
28
|
+
| Component | Description |
|
|
29
|
+
|-----------|-------------|
|
|
30
|
+
| `AgentRunner` | Multi-model agentic loop with tool dispatch, swarm routing, reflection |
|
|
31
|
+
| `ModelClient` | Streaming OpenAI-compatible client with fallback rotation + thinking mode |
|
|
32
|
+
| `ModelRegistry` | Dynamic model discovery, tier classification, cost tracking (356 models) |
|
|
33
|
+
| `SwarmRouter` | LLM-based task decomposition + parallel sub-agent execution |
|
|
34
|
+
| `SessionManager` | Conversation history with 3-tier smart compaction + turbo headroom tracking |
|
|
35
|
+
| `MemoryStore` | SQLite long-term memory (cross-session, persistent) |
|
|
36
|
+
| `GoalManager` | Persistent cross-session goals injected into every turn |
|
|
37
|
+
| `ContextStore` | Ephemeral task context folders (auto-deleted on completion) |
|
|
38
|
+
| `AgentScheduler` | Cron + price-trigger autonomous task scheduling |
|
|
39
|
+
| `MCPServer` | Model Context Protocol server (Claude Desktop / Cursor compatible) |
|
|
40
|
+
| **18 built-in tools** | Prices, candles+TA, news, Fear&Greed, funding rates, BTC mempool, DeFi TVL, Solana TPS, model management, goals, tasks, scheduling |
|
|
41
|
+
| Ink TUI | Terminal UI with live ticker, context pressure monitor, approval gates |
|
|
42
|
+
|
|
43
|
+
For wallets, vault, on-chain signing, and the full 28-tool trading suite → [JellyOS full project](https://github.com/jelly-chain/JellyOS)
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install -g @jellyos/agent
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
mkdir -p ~/.jelly
|
|
57
|
+
echo "OPENROUTER_API_KEY=sk-or-..." > ~/.jelly/.env
|
|
58
|
+
jellyos
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Headless Mode
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Single turn, print to stdout, exit
|
|
65
|
+
jellyos --headless "what is the current ETH price and RSI on the 1h chart?"
|
|
66
|
+
|
|
67
|
+
# Use in scripts
|
|
68
|
+
RESULT=$(jellyos --headless "summarize BTC market conditions")
|
|
69
|
+
echo "$RESULT"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## MCP Server (Claude Desktop / Cursor)
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Start MCP server — exposes all 18 tools to any MCP client
|
|
76
|
+
jellyos-mcp
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"mcpServers": {
|
|
83
|
+
"jellyos": {
|
|
84
|
+
"command": "jellyos-mcp"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Configuration (`~/.jelly/.env`)
|
|
93
|
+
|
|
94
|
+
```env
|
|
95
|
+
# AI Provider (pick one)
|
|
96
|
+
OPENROUTER_API_KEY=sk-or-... # recommended — 356 models
|
|
97
|
+
ANTHROPIC_API_KEY=sk-ant-... # direct Anthropic
|
|
98
|
+
OPENAI_API_KEY=sk-... # direct OpenAI
|
|
99
|
+
OPENAI_BASE_URL=http://localhost:11434/v1 # Ollama / local
|
|
100
|
+
|
|
101
|
+
# Optional model pool (up to 5, rotated on rate-limit)
|
|
102
|
+
JELLY_MODEL_1=anthropic/claude-opus-4.7
|
|
103
|
+
JELLY_MODEL_2=openai/gpt-5.5
|
|
104
|
+
JELLY_MODEL_3=google/gemini-3.5-flash
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Built-in Tools
|
|
110
|
+
|
|
111
|
+
| Tool | Description | Source |
|
|
112
|
+
|------|-------------|--------|
|
|
113
|
+
| `get_prices` | Live prices + 24h change | Binance + CoinGecko |
|
|
114
|
+
| `get_candles` | OHLCV data + RSI/MACD/Bollinger analysis | Binance |
|
|
115
|
+
| `get_top_movers` | Largest 24h movers | Binance |
|
|
116
|
+
| `get_market_overview` | Aggregated market summary | Multi-source |
|
|
117
|
+
| `get_news` | Headlines + sentiment scoring | CoinDesk/CoinTelegraph/TheBlock |
|
|
118
|
+
| `get_fear_greed` | Fear & Greed Index (7-day history) | alternative.me |
|
|
119
|
+
| `get_funding_rates` | Perp funding rates (long/short bias) | Binance |
|
|
120
|
+
| `get_btc_mempool` | BTC pending txs + fee rates | mempool.space |
|
|
121
|
+
| `get_defi_tvl` | DeFi TVL by chain | DeFiLlama |
|
|
122
|
+
| `get_solana_stats` | Solana TPS + network health | Solana RPC |
|
|
123
|
+
| `analyze_ta` | RSI, MACD, Bollinger, EMA, ATR on price arrays | Local |
|
|
124
|
+
| `list_models` | Search 356 available AI models | OpenRouter |
|
|
125
|
+
| `pick_model` | Find cheapest model for requirements | OpenRouter |
|
|
126
|
+
| `set_goal` / `list_goals` / `complete_goal` | Persistent cross-session goals | Local |
|
|
127
|
+
| `schedule_task` / `list_schedule` | Cron + price-trigger scheduling | Local |
|
|
128
|
+
| `read_task_context` / `list_tasks` | Ephemeral task context folders | Local |
|
|
129
|
+
| `cost_report` | Session + lifetime token usage | Local |
|
|
130
|
+
|
|
131
|
+
All data tools are free — no API keys required.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Effect Levels
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
/effect eco # minimal tool calls, cheapest models
|
|
139
|
+
/effect normal # balanced (default)
|
|
140
|
+
/effect turbo # 2 parallel sub-agents for complex tasks
|
|
141
|
+
/effect max # 5 agents + thinking models for deep research
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Extension API
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import { Type } from "@jellyos/agent";
|
|
150
|
+
import type { ExtensionAPI } from "@jellyos/agent";
|
|
151
|
+
|
|
152
|
+
export default function (agent: ExtensionAPI) {
|
|
153
|
+
agent.setSystemPrompt("You are a DeFi yield optimizer.");
|
|
154
|
+
|
|
155
|
+
agent.registerTool({
|
|
156
|
+
name: "get_apy",
|
|
157
|
+
label: "Get APY",
|
|
158
|
+
description: "Fetch current APY for a DeFi protocol",
|
|
159
|
+
requiresApproval: false, // set true for money-moving tools
|
|
160
|
+
parameters: Type.Object({
|
|
161
|
+
protocol: Type.String({ description: "Protocol name e.g. aave" }),
|
|
162
|
+
}),
|
|
163
|
+
async execute(_id, { protocol }) {
|
|
164
|
+
const res = await fetch(`https://api.llama.fi/protocol/${protocol}`);
|
|
165
|
+
const data = await res.json() as Record<string, unknown>;
|
|
166
|
+
return {
|
|
167
|
+
content: [{ type: "text", text: `${protocol}: ${JSON.stringify(data)}` }],
|
|
168
|
+
details: {},
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
agent.on("session_start", async (ctx) => {
|
|
174
|
+
ctx.ui.setStatus("yields", "ready");
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
jellyos --extension ./my-extension.ts
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## REPL Commands
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
/help List commands
|
|
189
|
+
/effect [level] eco | normal | turbo | max
|
|
190
|
+
/prices [symbols] Quick price check
|
|
191
|
+
/news Latest headlines + sentiment
|
|
192
|
+
/goals List active goals
|
|
193
|
+
/goal add <text> Add a persistent goal
|
|
194
|
+
/goal done <id> Mark goal complete
|
|
195
|
+
/schedule List scheduled tasks
|
|
196
|
+
/tasks List active context folders
|
|
197
|
+
/traces Show last 5 agent traces with timing
|
|
198
|
+
/memory <query> Search long-term memory
|
|
199
|
+
/models [query] Search available AI models
|
|
200
|
+
/cost Session + lifetime cost report
|
|
201
|
+
/approve | /deny Respond to tool approval gates
|
|
202
|
+
/palette Full command + tool list
|
|
203
|
+
/clear Clear conversation history
|
|
204
|
+
Escape Abort in-flight model stream
|
|
205
|
+
Ctrl-C Exit
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
MIT
|
package/bin/jellyos-mcp
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* jellyos-mcp — JellyOS MCP server entry point.
|
|
4
|
+
* Exposes all JellyOS tools via the Model Context Protocol (stdio transport).
|
|
5
|
+
* Compatible with Claude Desktop, Cursor, Continue, and any MCP client.
|
|
6
|
+
*/
|
|
7
|
+
import { createRequire } from "module";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
import { dirname, join } from "path";
|
|
10
|
+
import { existsSync } from "fs";
|
|
11
|
+
|
|
12
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const require = createRequire(import.meta.url);
|
|
14
|
+
|
|
15
|
+
const distEntry = join(__dirname, "..", "dist", "mcp", "entry.js");
|
|
16
|
+
const srcEntry = join(__dirname, "..", "src", "mcp", "entry.ts");
|
|
17
|
+
|
|
18
|
+
if (existsSync(distEntry)) {
|
|
19
|
+
await import(distEntry);
|
|
20
|
+
} else if (existsSync(srcEntry)) {
|
|
21
|
+
const { register } = await import("tsx/esm");
|
|
22
|
+
await import(srcEntry);
|
|
23
|
+
} else {
|
|
24
|
+
process.stderr.write("JellyOS MCP: run 'npm run build' first\n");
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
@@ -24,6 +24,12 @@ export interface ToolDef<P extends TSchema = TSchema> {
|
|
|
24
24
|
label: string;
|
|
25
25
|
description: string;
|
|
26
26
|
parameters: P;
|
|
27
|
+
/**
|
|
28
|
+
* #10: If true, the agent pauses before executing this tool and asks the
|
|
29
|
+
* user for confirmation. Use for any tool that moves money or signs txs.
|
|
30
|
+
* The REPL renders an [APPROVE] prompt — user types y/n within 60 seconds.
|
|
31
|
+
*/
|
|
32
|
+
requiresApproval?: boolean;
|
|
27
33
|
execute(id: string, params: Static<P>): Promise<ToolContent>;
|
|
28
34
|
}
|
|
29
35
|
export interface TuiHeader {
|
|
@@ -54,6 +60,11 @@ export interface UIContext {
|
|
|
54
60
|
* Pi compat — accepted but engine renders its own Ink header.
|
|
55
61
|
*/
|
|
56
62
|
setHeader(factory: HeaderFactory): void;
|
|
63
|
+
/**
|
|
64
|
+
* Open the interactive model selector overlay.
|
|
65
|
+
* @param query Optional initial search query to pre-filter the list.
|
|
66
|
+
*/
|
|
67
|
+
showModelSelector(query?: string): void;
|
|
57
68
|
theme: ThemeContext;
|
|
58
69
|
}
|
|
59
70
|
export interface CommandContext {
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* cli.ts — JellyOS entry point.
|
|
3
3
|
* Completely standalone — all outbound, no inbound ports exposed.
|
|
4
4
|
*/
|
|
5
|
-
import { readFileSync, existsSync } from "node:fs";
|
|
5
|
+
import { readFileSync, existsSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { resolve, join } from "node:path";
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import { render } from "ink";
|
|
@@ -14,6 +14,9 @@ import { App } from "./tui/App.js";
|
|
|
14
14
|
import { T } from "./tui/theme.js";
|
|
15
15
|
import { modelRegistry } from "./models/ModelRegistry.js";
|
|
16
16
|
import { CostTracker } from "./models/CostTracker.js";
|
|
17
|
+
import { AgentRunner } from "./runner/AgentRunner.js";
|
|
18
|
+
import { SessionManager } from "./session/SessionManager.js";
|
|
19
|
+
import { makeTheme } from "./tui/theme.js";
|
|
17
20
|
// ── Load env vars from ~/.jelly/.env ─────────────────────────────────────────
|
|
18
21
|
const JELLY_HOME = process.env.JELLYOS_HOME ?? join(homedir(), ".jelly");
|
|
19
22
|
const envPath = join(JELLY_HOME, ".env");
|
|
@@ -77,16 +80,89 @@ function loadContext() {
|
|
|
77
80
|
return { effectLevel: "normal", chain: "ethereum" };
|
|
78
81
|
try {
|
|
79
82
|
const ctx = JSON.parse(readFileSync(ctxPath, "utf-8"));
|
|
83
|
+
if (ctx.model && typeof ctx.model === "string") {
|
|
84
|
+
process.env.DEFAULT_MODEL = ctx.model;
|
|
85
|
+
try {
|
|
86
|
+
const ef = join(JELLY_HOME, ".env");
|
|
87
|
+
const c = existsSync(ef) ? readFileSync(ef, "utf-8") : "";
|
|
88
|
+
const re = /^DEFAULT_MODEL=.*$/m;
|
|
89
|
+
writeFileSync(ef, re.test(c) ? c.replace(re, `DEFAULT_MODEL=${ctx.model}`) : c + `\nDEFAULT_MODEL=${ctx.model}\n`, "utf-8");
|
|
90
|
+
}
|
|
91
|
+
catch { /* non-fatal */ }
|
|
92
|
+
}
|
|
80
93
|
return { effectLevel: ctx.effect_level ?? "normal", chain: ctx.active_chain ?? "ethereum" };
|
|
81
94
|
}
|
|
82
95
|
catch {
|
|
83
96
|
return { effectLevel: "normal", chain: "ethereum" };
|
|
84
97
|
}
|
|
85
98
|
}
|
|
86
|
-
// ──
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
99
|
+
// ── Headless mode (#26) ─────────────────────────────────────────────────────
|
|
100
|
+
const headlessIdx = args.indexOf("--headless");
|
|
101
|
+
const headlessMsg = headlessIdx >= 0 ? args[headlessIdx + 1] : null;
|
|
102
|
+
if (headlessMsg) {
|
|
103
|
+
(async () => {
|
|
104
|
+
if (existsSync(envPath))
|
|
105
|
+
loadDotenv({ path: envPath, override: false });
|
|
106
|
+
const registry = new Registry();
|
|
107
|
+
if (extensionPath) {
|
|
108
|
+
try {
|
|
109
|
+
await loadExtension(extensionPath, registry);
|
|
110
|
+
}
|
|
111
|
+
catch (e) {
|
|
112
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
113
|
+
process.stderr.write(`Extension load failed: ${msg}\n`);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
await modelRegistry.initialise();
|
|
118
|
+
const session = new SessionManager();
|
|
119
|
+
const prompt = systemPrompt || registry.getSystemPrompt() || "You are JellyOS, an autonomous AI trading agent.";
|
|
120
|
+
session.setSystemPrompt(prompt);
|
|
121
|
+
const theme = makeTheme();
|
|
122
|
+
const nullUi = {
|
|
123
|
+
notify: () => { },
|
|
124
|
+
setStatus: () => { },
|
|
125
|
+
setTheme: () => { },
|
|
126
|
+
setHeader: () => { },
|
|
127
|
+
showModelSelector: () => { },
|
|
128
|
+
theme,
|
|
129
|
+
};
|
|
130
|
+
const sessionCtx = {
|
|
131
|
+
ui: nullUi,
|
|
132
|
+
hasUI: false,
|
|
133
|
+
config: { OPENROUTER_API_KEY: process.env.OPENROUTER_API_KEY },
|
|
134
|
+
};
|
|
135
|
+
// Fire session_start hooks
|
|
136
|
+
await registry.fireHook("session_start", sessionCtx);
|
|
137
|
+
session.setSystemPrompt(registry.getSystemPrompt() || prompt);
|
|
138
|
+
let exitCode = 0;
|
|
139
|
+
const runner = new AgentRunner(registry, session, (event) => {
|
|
140
|
+
if (event.type === "text_delta")
|
|
141
|
+
process.stdout.write(event.text);
|
|
142
|
+
if (event.type === "turn_done")
|
|
143
|
+
process.stdout.write("\n");
|
|
144
|
+
if (event.type === "error") {
|
|
145
|
+
process.stderr.write(`\nError: ${event.message}\n`);
|
|
146
|
+
exitCode = 1;
|
|
147
|
+
}
|
|
148
|
+
}, sessionCtx, "normal", modelRegistry);
|
|
149
|
+
try {
|
|
150
|
+
await runner.run(headlessMsg);
|
|
151
|
+
}
|
|
152
|
+
catch (e) {
|
|
153
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
154
|
+
process.stderr.write(`Runner error: ${msg}\n`);
|
|
155
|
+
exitCode = 1;
|
|
156
|
+
}
|
|
157
|
+
await registry.fireHook("session_end", sessionCtx);
|
|
158
|
+
process.exit(exitCode);
|
|
159
|
+
})();
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
// ── Boot (interactive TUI) ────────────────────────────────────────────────────
|
|
163
|
+
(async () => {
|
|
164
|
+
console.clear();
|
|
165
|
+
console.log(T.accent(`
|
|
90
166
|
██╗███████╗██╗ ██╗ ██╗ ██╗ ██████╗ ███████╗
|
|
91
167
|
██║██╔════╝██║ ██║ ╚██╗ ██╔╝ ██╔═══██╗██╔════╝
|
|
92
168
|
██║█████╗ ██║ ██║ ╚████╔╝ ██║ ██║███████╗
|
|
@@ -94,50 +170,52 @@ function loadContext() {
|
|
|
94
170
|
╚█████╔╝███████╗███████╗██║ ██║ ╚██████╔╝███████║
|
|
95
171
|
╚════╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝
|
|
96
172
|
`));
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
173
|
+
console.log(T.muted(" Standalone AI trading agent — all local, zero exposure\n"));
|
|
174
|
+
const registry = new Registry();
|
|
175
|
+
const { effectLevel, chain } = loadContext();
|
|
176
|
+
let _notifyFn = null;
|
|
177
|
+
let _setStatusFn = null;
|
|
178
|
+
let _showModelSelectorFn = null;
|
|
179
|
+
if (extensionPath) {
|
|
180
|
+
try {
|
|
181
|
+
console.log(T.muted(` Loading: ${extensionPath}`));
|
|
182
|
+
await loadExtension(extensionPath, registry, {
|
|
183
|
+
onNotify: (msg) => { _notifyFn?.(msg); },
|
|
184
|
+
onStatusUpdate: (k, v) => { _setStatusFn?.(k, v); },
|
|
185
|
+
onShowModelSelector: (q) => { _showModelSelectorFn?.(q); },
|
|
186
|
+
});
|
|
187
|
+
console.log(T.success(` ✓ ${registry.listTools().length} tools · ${registry.listCommands().length} commands`));
|
|
188
|
+
}
|
|
189
|
+
catch (e) {
|
|
190
|
+
console.error(T.error(` ✗ Extension load failed: ${e.message}`));
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
112
193
|
}
|
|
113
|
-
|
|
114
|
-
console.
|
|
115
|
-
|
|
194
|
+
else {
|
|
195
|
+
console.log(T.warn(" No extension found — base agent only."));
|
|
196
|
+
console.log(T.muted(" Run from jellyos project root or pass --extension path\n"));
|
|
116
197
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
onStatusReady: (fn) => { _setStatusFn = fn; },
|
|
141
|
-
}), { exitOnCtrlC: false });
|
|
142
|
-
})();
|
|
198
|
+
if (!process.env.OPENROUTER_API_KEY && !process.env.ANTHROPIC_API_KEY && !process.env.OPENAI_API_KEY) {
|
|
199
|
+
console.log(T.warn(" No API key found. Set OPENROUTER_API_KEY in ~/.jelly/.env\n"));
|
|
200
|
+
}
|
|
201
|
+
// ── Initialise model registry & cost tracker ─────────────────────────────
|
|
202
|
+
const costTracker = new CostTracker(modelRegistry);
|
|
203
|
+
console.log(T.muted(" Discovering available models via OpenRouter…"));
|
|
204
|
+
await modelRegistry.initialise();
|
|
205
|
+
console.log(T.success(` ✓ ${modelRegistry.modelCount} models available \n`));
|
|
206
|
+
await new Promise(r => setTimeout(r, 500));
|
|
207
|
+
console.clear();
|
|
208
|
+
render(React.createElement(App, {
|
|
209
|
+
registry,
|
|
210
|
+
systemPrompt,
|
|
211
|
+
effectLevel,
|
|
212
|
+
chain,
|
|
213
|
+
modelReg: modelRegistry,
|
|
214
|
+
costTracker,
|
|
215
|
+
onNotifyReady: (fn) => { _notifyFn = fn; },
|
|
216
|
+
onStatusReady: (fn) => { _setStatusFn = fn; },
|
|
217
|
+
onModelSelectorReady: (fn) => { _showModelSelectorFn = fn; },
|
|
218
|
+
}), { exitOnCtrlC: false });
|
|
219
|
+
})();
|
|
220
|
+
} // end headless else
|
|
143
221
|
//# sourceMappingURL=cli.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -12,17 +12,30 @@ export { Registry } from "./api/Registry.js";
|
|
|
12
12
|
export { AgentRunner } from "./runner/AgentRunner.js";
|
|
13
13
|
export { ModelClient, resolveModelConfig } from "./runner/ModelClient.js";
|
|
14
14
|
export { ToolDispatcher } from "./runner/ToolDispatcher.js";
|
|
15
|
-
export { SessionManager } from "./session/SessionManager.js";
|
|
16
15
|
export { loadExtension } from "./loader.js";
|
|
17
16
|
export { makeTheme, T, JELLY_COLORS } from "./tui/theme.js";
|
|
18
17
|
export { ModelRegistry, modelRegistry, classifyModel } from "./models/ModelRegistry.js";
|
|
19
18
|
export { CostTracker } from "./models/CostTracker.js";
|
|
20
19
|
export type { OpenRouterModel, TieredModel, TieredPool, ModelTier } from "./models/ModelRegistry.js";
|
|
21
20
|
export type { UsageEntry, SessionUsage, LifetimeUsage } from "./models/CostTracker.js";
|
|
22
|
-
export { fullAnalysis, rsi, macd, ema, sma, bollingerBands, atr } from "./tools/TechnicalAnalysis.js";
|
|
21
|
+
export { fullAnalysis, rsi, macd, ema, sma, bollingerBands, atr, getCandlesTool, getCandlesParams } from "./tools/TechnicalAnalysis.js";
|
|
23
22
|
export { PriceFeed, priceFeed, getPricesTool, topMoversTool, marketOverviewTool } from "./tools/PriceFeed.js";
|
|
24
23
|
export { NewsFeed, newsFeed, getNewsTool, scoreSentiment } from "./tools/NewsSentiment.js";
|
|
24
|
+
export { getFearGreedTool, fundingRatesParams, getFundingRatesTool, getBtcMempoolTool, getDefiTvlTool, getSolanaStatsTool, } from "./tools/MarketSentiment.js";
|
|
25
25
|
export type { OHLCV, AnalysisResult } from "./tools/TechnicalAnalysis.js";
|
|
26
26
|
export type { PriceTick } from "./tools/PriceFeed.js";
|
|
27
27
|
export type { NewsItem, SentimentReport } from "./tools/NewsSentiment.js";
|
|
28
|
+
export { SessionManager } from "./session/SessionManager.js";
|
|
29
|
+
export { MemoryStore, memoryStore } from "./session/MemoryStore.js";
|
|
30
|
+
export { GoalManager, goalManager } from "./session/GoalManager.js";
|
|
31
|
+
export { ContextStore, contextStore } from "./session/ContextStore.js";
|
|
32
|
+
export type { MemoryEntry, RecentSession } from "./session/MemoryStore.js";
|
|
33
|
+
export type { Goal } from "./session/GoalManager.js";
|
|
34
|
+
export type { TaskContext } from "./session/ContextStore.js";
|
|
35
|
+
export type { ContextPressure } from "./session/SessionManager.js";
|
|
36
|
+
export { MCPServer } from "./mcp/server.js";
|
|
37
|
+
export { AgentScheduler, agentScheduler } from "./scheduler/AgentScheduler.js";
|
|
38
|
+
export type { ScheduledTask, PriceTrigger } from "./scheduler/AgentScheduler.js";
|
|
39
|
+
export { Tracer } from "./telemetry/Tracer.js";
|
|
40
|
+
export type { Span, Trace } from "./telemetry/Tracer.js";
|
|
28
41
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -15,8 +15,6 @@ export { Registry } from "./api/Registry.js";
|
|
|
15
15
|
export { AgentRunner } from "./runner/AgentRunner.js";
|
|
16
16
|
export { ModelClient, resolveModelConfig } from "./runner/ModelClient.js";
|
|
17
17
|
export { ToolDispatcher } from "./runner/ToolDispatcher.js";
|
|
18
|
-
// Session
|
|
19
|
-
export { SessionManager } from "./session/SessionManager.js";
|
|
20
18
|
// Loader (for embedding the agent in other tools)
|
|
21
19
|
export { loadExtension } from "./loader.js";
|
|
22
20
|
// Theme
|
|
@@ -25,7 +23,19 @@ export { makeTheme, T, JELLY_COLORS } from "./tui/theme.js";
|
|
|
25
23
|
export { ModelRegistry, modelRegistry, classifyModel } from "./models/ModelRegistry.js";
|
|
26
24
|
export { CostTracker } from "./models/CostTracker.js";
|
|
27
25
|
// Tools
|
|
28
|
-
export { fullAnalysis, rsi, macd, ema, sma, bollingerBands, atr } from "./tools/TechnicalAnalysis.js";
|
|
26
|
+
export { fullAnalysis, rsi, macd, ema, sma, bollingerBands, atr, getCandlesTool, getCandlesParams } from "./tools/TechnicalAnalysis.js";
|
|
29
27
|
export { PriceFeed, priceFeed, getPricesTool, topMoversTool, marketOverviewTool } from "./tools/PriceFeed.js";
|
|
30
28
|
export { NewsFeed, newsFeed, getNewsTool, scoreSentiment } from "./tools/NewsSentiment.js";
|
|
29
|
+
export { getFearGreedTool, fundingRatesParams, getFundingRatesTool, getBtcMempoolTool, getDefiTvlTool, getSolanaStatsTool, } from "./tools/MarketSentiment.js";
|
|
30
|
+
// Session / Memory / Goals / Context
|
|
31
|
+
export { SessionManager } from "./session/SessionManager.js";
|
|
32
|
+
export { MemoryStore, memoryStore } from "./session/MemoryStore.js";
|
|
33
|
+
export { GoalManager, goalManager } from "./session/GoalManager.js";
|
|
34
|
+
export { ContextStore, contextStore } from "./session/ContextStore.js";
|
|
35
|
+
// MCP server
|
|
36
|
+
export { MCPServer } from "./mcp/server.js";
|
|
37
|
+
// Scheduler
|
|
38
|
+
export { AgentScheduler, agentScheduler } from "./scheduler/AgentScheduler.js";
|
|
39
|
+
// Telemetry
|
|
40
|
+
export { Tracer } from "./telemetry/Tracer.js";
|
|
31
41
|
//# sourceMappingURL=index.js.map
|
package/dist/loader.d.ts
CHANGED
|
@@ -7,17 +7,10 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { Registry } from "./api/Registry.js";
|
|
9
9
|
export interface LoaderOptions {
|
|
10
|
-
/**
|
|
11
|
-
* Called when the extension uses ui.setStatus(key, value).
|
|
12
|
-
* The App component passes a state-setter here so status badges update live.
|
|
13
|
-
*/
|
|
14
10
|
onStatusUpdate?: (key: string, value: string) => void;
|
|
15
|
-
/**
|
|
16
|
-
* Called when the extension calls ui.notify(message).
|
|
17
|
-
* Before the TUI is mounted, the App wires this to React state.
|
|
18
|
-
* We store the latest notifier here so the extension can call it any time.
|
|
19
|
-
*/
|
|
20
11
|
onNotify?: (message: string) => void;
|
|
12
|
+
/** Called when the extension calls ui.showModelSelector(query). */
|
|
13
|
+
onShowModelSelector?: (query?: string) => void;
|
|
21
14
|
}
|
|
22
15
|
export declare function loadExtension(extensionPath: string, registry: Registry, opts?: LoaderOptions): Promise<void>;
|
|
23
16
|
//# sourceMappingURL=loader.d.ts.map
|
package/dist/loader.js
CHANGED
|
@@ -15,14 +15,15 @@ export async function loadExtension(extensionPath, registry, opts = {}) {
|
|
|
15
15
|
throw new Error(`Extension file not found: ${abs}`);
|
|
16
16
|
}
|
|
17
17
|
const theme = makeTheme();
|
|
18
|
-
// Live-updateable notifier — App.tsx replaces this once mounted
|
|
19
18
|
let _notify = opts.onNotify ?? ((_msg) => { });
|
|
20
19
|
let _setStatus = opts.onStatusUpdate ?? ((_k, _v) => { });
|
|
20
|
+
let _showModelSelector = opts.onShowModelSelector ?? ((_q) => { });
|
|
21
21
|
const ui = {
|
|
22
22
|
notify(message) { _notify(message); },
|
|
23
23
|
setStatus(key, value) { _setStatus(key, value); },
|
|
24
24
|
setTheme(_name) { },
|
|
25
25
|
setHeader(_factory) { },
|
|
26
|
+
showModelSelector(query) { _showModelSelector(query); },
|
|
26
27
|
theme,
|
|
27
28
|
};
|
|
28
29
|
const api = {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP server entry — bootstraps the Registry with built-in tools
|
|
3
|
+
* and starts the MCP stdio server.
|
|
4
|
+
*/
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { config as loadDotenv } from "dotenv";
|
|
9
|
+
import { Registry } from "../api/Registry.js";
|
|
10
|
+
import { loadExtension } from "../loader.js";
|
|
11
|
+
import { MCPServer } from "./server.js";
|
|
12
|
+
import { modelRegistry } from "../models/ModelRegistry.js";
|
|
13
|
+
import { getPricesTool, topMoversTool, marketOverviewTool, getPricesParams, topMoversParams, marketOverviewParams, priceFeed, } from "../tools/PriceFeed.js";
|
|
14
|
+
import { getNewsTool, getNewsParams, newsFeed } from "../tools/NewsSentiment.js";
|
|
15
|
+
import { getCandlesParams, getCandlesTool, analyzeTAParams, fullAnalysis } from "../tools/TechnicalAnalysis.js";
|
|
16
|
+
import { getFearGreedTool, fearGreedParams, getFundingRatesTool, fundingRatesParams, getBtcMempoolTool, btcMempoolParams, getDefiTvlTool, defiTvlParams, getSolanaStatsTool, solanaStatsParams, } from "../tools/MarketSentiment.js";
|
|
17
|
+
const JELLY_HOME = process.env.JELLYOS_HOME ?? join(homedir(), ".jelly");
|
|
18
|
+
const envPath = join(JELLY_HOME, ".env");
|
|
19
|
+
if (existsSync(envPath))
|
|
20
|
+
loadDotenv({ path: envPath, override: false });
|
|
21
|
+
const registry = new Registry();
|
|
22
|
+
// Load optional extension from --extension arg
|
|
23
|
+
const extIdx = process.argv.indexOf("--extension");
|
|
24
|
+
const extPath = extIdx >= 0 ? process.argv[extIdx + 1] : null;
|
|
25
|
+
if (extPath && existsSync(extPath)) {
|
|
26
|
+
try {
|
|
27
|
+
await loadExtension(extPath, registry);
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
process.stderr.write(`[MCP] Extension load failed: ${e}\n`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Register all built-in tools
|
|
34
|
+
await modelRegistry.initialise();
|
|
35
|
+
const mk = modelRegistry;
|
|
36
|
+
registry.addTool({ name: "list_models", label: "List Models", description: "Search available AI models.", parameters: mk["listModelsParams"], execute: (id, p) => mk["listModelsTool"](id, p) });
|
|
37
|
+
registry.addTool({ name: "get_prices", label: "Get Prices", description: "Get live crypto prices.", parameters: getPricesParams, execute: (id, p) => getPricesTool(id, p) });
|
|
38
|
+
registry.addTool({ name: "get_top_movers", label: "Top Movers", description: "Top 24h price movers.", parameters: topMoversParams, execute: (id, p) => topMoversTool(id, p) });
|
|
39
|
+
registry.addTool({ name: "market_overview", label: "Market Overview", description: "Aggregated market data.", parameters: marketOverviewParams, execute: () => marketOverviewTool() });
|
|
40
|
+
registry.addTool({ name: "get_news", label: "Get News", description: "Crypto news + sentiment.", parameters: getNewsParams, execute: (id, p) => getNewsTool(id, p) });
|
|
41
|
+
registry.addTool({ name: "get_candles", label: "Get Candles", description: "OHLCV + TA analysis from Binance.", parameters: getCandlesParams, execute: (id, p) => getCandlesTool(id, p) });
|
|
42
|
+
registry.addTool({ name: "get_fear_greed", label: "Fear & Greed", description: "Crypto fear & greed index.", parameters: fearGreedParams, execute: (id, p) => getFearGreedTool(id, p) });
|
|
43
|
+
registry.addTool({ name: "get_funding_rates", label: "Funding Rates", description: "Perp funding rates.", parameters: fundingRatesParams, execute: (id, p) => getFundingRatesTool(id, p) });
|
|
44
|
+
registry.addTool({ name: "get_btc_mempool", label: "BTC Mempool", description: "BTC mempool + fees.", parameters: btcMempoolParams, execute: () => getBtcMempoolTool() });
|
|
45
|
+
registry.addTool({ name: "get_defi_tvl", label: "DeFi TVL", description: "DeFiLlama TVL.", parameters: defiTvlParams, execute: (id, p) => getDefiTvlTool(id, p) });
|
|
46
|
+
registry.addTool({ name: "get_solana_stats", label: "Solana Stats", description: "Solana TPS + health.", parameters: solanaStatsParams, execute: () => getSolanaStatsTool() });
|
|
47
|
+
registry.addTool({
|
|
48
|
+
name: "analyze_ta", label: "Technical Analysis", description: "RSI, MACD, Bollinger on price arrays.",
|
|
49
|
+
parameters: analyzeTAParams,
|
|
50
|
+
execute: async (_id, p) => {
|
|
51
|
+
const params = p;
|
|
52
|
+
const closes = params.prices;
|
|
53
|
+
const candles = closes.map((c, i) => ({
|
|
54
|
+
timestamp: i, open: c, high: (params.highs?.[i] ?? c), low: (params.lows?.[i] ?? c), close: c, volume: (params.volumes?.[i] ?? 0),
|
|
55
|
+
}));
|
|
56
|
+
const results = fullAnalysis(candles);
|
|
57
|
+
const text = results.map(r => {
|
|
58
|
+
const s = r.signal === "bullish" ? "🟢" : r.signal === "bearish" ? "🔴" : "⚪";
|
|
59
|
+
return `${s} ${r.indicator}: ${typeof r.value === "number" ? r.value.toFixed(2) : String(r.value)}`;
|
|
60
|
+
}).join("\n");
|
|
61
|
+
return { content: [{ type: "text", text: `Technical Analysis:\n${text}` }], details: {} };
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
// Start price + news feeds
|
|
65
|
+
priceFeed.track("btc", "eth", "sol", "bnb", "matic", "arb", "op", "avax");
|
|
66
|
+
priceFeed.start();
|
|
67
|
+
newsFeed.start();
|
|
68
|
+
// Start MCP server
|
|
69
|
+
const server = new MCPServer(registry);
|
|
70
|
+
await server.run();
|
|
71
|
+
//# sourceMappingURL=entry.js.map
|