@jellyos/agent 0.1.1
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 +375 -0
- package/bin/jellyagent +31 -0
- package/dist/api/ExtensionAPI.d.ts +92 -0
- package/dist/api/ExtensionAPI.d.ts.map +1 -0
- package/dist/api/ExtensionAPI.js +15 -0
- package/dist/api/ExtensionAPI.js.map +1 -0
- package/dist/api/Registry.d.ts +54 -0
- package/dist/api/Registry.d.ts.map +1 -0
- package/dist/api/Registry.js +101 -0
- package/dist/api/Registry.js.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +134 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +23 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +84 -0
- package/dist/loader.js.map +1 -0
- package/dist/runner/AgentRunner.d.ts +66 -0
- package/dist/runner/AgentRunner.d.ts.map +1 -0
- package/dist/runner/AgentRunner.js +179 -0
- package/dist/runner/AgentRunner.js.map +1 -0
- package/dist/runner/ModelClient.d.ts +77 -0
- package/dist/runner/ModelClient.d.ts.map +1 -0
- package/dist/runner/ModelClient.js +224 -0
- package/dist/runner/ModelClient.js.map +1 -0
- package/dist/runner/SwarmRouter.d.ts +58 -0
- package/dist/runner/SwarmRouter.d.ts.map +1 -0
- package/dist/runner/SwarmRouter.js +153 -0
- package/dist/runner/SwarmRouter.js.map +1 -0
- package/dist/runner/ToolDispatcher.d.ts +19 -0
- package/dist/runner/ToolDispatcher.d.ts.map +1 -0
- package/dist/runner/ToolDispatcher.js +64 -0
- package/dist/runner/ToolDispatcher.js.map +1 -0
- package/dist/session/SessionManager.d.ts +23 -0
- package/dist/session/SessionManager.d.ts.map +1 -0
- package/dist/session/SessionManager.js +50 -0
- package/dist/session/SessionManager.js.map +1 -0
- package/dist/tui/App.d.ts +18 -0
- package/dist/tui/App.d.ts.map +1 -0
- package/dist/tui/App.js +188 -0
- package/dist/tui/App.js.map +1 -0
- package/dist/tui/REPL.d.ts +22 -0
- package/dist/tui/REPL.d.ts.map +1 -0
- package/dist/tui/REPL.js +46 -0
- package/dist/tui/REPL.js.map +1 -0
- package/dist/tui/StatusBar.d.ts +16 -0
- package/dist/tui/StatusBar.d.ts.map +1 -0
- package/dist/tui/StatusBar.js +15 -0
- package/dist/tui/StatusBar.js.map +1 -0
- package/dist/tui/theme.d.ts +30 -0
- package/dist/tui/theme.d.ts.map +1 -0
- package/dist/tui/theme.js +41 -0
- package/dist/tui/theme.js.map +1 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# @jellyos/agent
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
<pre>
|
|
6
|
+
██╗███████╗██╗ ██╗ ██╗ ██╗ ██████╗ ███████╗
|
|
7
|
+
██║██╔════╝██║ ██║ ╚██╗ ██╔╝ ██╔═══██╗██╔════╝
|
|
8
|
+
██║█████╗ ██║ ██║ ╚████╔╝ ██║ ██║███████╗
|
|
9
|
+
██ ██║██╔══╝ ██║ ██║ ╚██╔╝ ██║ ██║╚════██║
|
|
10
|
+
╚█████╔╝███████╗███████╗███████╗██║ ╚██████╔╝███████║
|
|
11
|
+
╚════╝ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═════╝ ╚══════╝
|
|
12
|
+
</pre>
|
|
13
|
+
|
|
14
|
+
**Standalone AI trading agent. Runs 100% locally. No server. No inbound ports. No cloud dependency.**
|
|
15
|
+
|
|
16
|
+
[](https://www.npmjs.com/package/@jellyos/agent)
|
|
17
|
+
[](https://nodejs.org)
|
|
18
|
+
[](LICENSE)
|
|
19
|
+
|
|
20
|
+
[Website](http://jelly-os.xyz/) • [Telegram](https://t.me/jellyxchain) • [X / Twitter](https://x.com/agentz010) • [API](https://jellychain.fun)
|
|
21
|
+
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## What is JellyOS?
|
|
27
|
+
|
|
28
|
+
JellyOS is an AI trading agent that runs entirely in your terminal. It connects to AI model providers and blockchain data APIs through outbound HTTP calls only — nothing is hosted, nothing listens for inbound connections, and your keys never leave your machine.
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
Your machine
|
|
32
|
+
├── jellyos (CLI)
|
|
33
|
+
│ ├── Ink TUI ← streaming terminal UI
|
|
34
|
+
│ ├── AgentRunner ← model loop + tool dispatcher
|
|
35
|
+
│ ├── SwarmRouter ← parallel sub-agent orchestration
|
|
36
|
+
│ ├── FeedManager ← 21 live data sources (background)
|
|
37
|
+
│ ├── SignalEngine ← cross-feed trading signals
|
|
38
|
+
│ ├── WalletManager ← EVM / Solana / Cosmos keypairs
|
|
39
|
+
│ ├── VaultManager ← AES-256-GCM encrypted profit vault
|
|
40
|
+
│ └── DashboardServer ← optional local WebSocket dashboard
|
|
41
|
+
│
|
|
42
|
+
├── ~/.jellyos/
|
|
43
|
+
│ ├── .env ← your API keys
|
|
44
|
+
│ ├── wallets/ ← local keypairs (never synced)
|
|
45
|
+
│ ├── vault/ ← encrypted vault file
|
|
46
|
+
│ └── context.json ← session state, watchlist, positions
|
|
47
|
+
│
|
|
48
|
+
└── Outbound only:
|
|
49
|
+
├── openrouter.ai ← AI model gateway (your key)
|
|
50
|
+
├── api.coingecko.com ← prices, trending
|
|
51
|
+
├── api.binance.com ← 24h tickers
|
|
52
|
+
├── api.llama.fi ← DeFi TVL
|
|
53
|
+
├── alchemy.com ← on-chain data (optional key)
|
|
54
|
+
├── mempool.space ← BTC mempool
|
|
55
|
+
├── etherscan.io ← ETH gas
|
|
56
|
+
└── ... 15 more sources
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Install
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm install -g @jellyos/agent
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Quick Start
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# 1. Create config directory and set your API key
|
|
71
|
+
mkdir -p ~/.jellyos
|
|
72
|
+
echo "OPENROUTER_API_KEY=sk-or-..." > ~/.jellyos/.env
|
|
73
|
+
|
|
74
|
+
# 2. Launch
|
|
75
|
+
jellyos
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The terminal boots with an ASCII header and drops you into the `jell>` prompt.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Using the Full JellyOS Stack
|
|
83
|
+
|
|
84
|
+
For wallets, vault, live feeds, and all 28 trading tools:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Clone the full project
|
|
88
|
+
git clone https://github.com/jelly-chain/JellyOS.git
|
|
89
|
+
cd JellyOS
|
|
90
|
+
|
|
91
|
+
# Install dependencies
|
|
92
|
+
npm install
|
|
93
|
+
|
|
94
|
+
# Run one-command setup (generates wallets, vault ceremony, writes ~/.jellyos/.env)
|
|
95
|
+
bash setup.sh # macOS / Linux
|
|
96
|
+
# or
|
|
97
|
+
powershell -ExecutionPolicy Bypass -File setup.ps1 # Windows
|
|
98
|
+
|
|
99
|
+
# Launch — auto-detects extensions/jellyos.ts
|
|
100
|
+
jellyos
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Configuration
|
|
106
|
+
|
|
107
|
+
All config lives in `~/.jellyos/.env`. The setup wizard creates this for you, or create it manually:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
cp .env.example ~/.jellyos/.env
|
|
111
|
+
nano ~/.jellyos/.env
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### AI Model Provider (pick one)
|
|
115
|
+
|
|
116
|
+
| Variable | Provider | Example models |
|
|
117
|
+
|----------|----------|----------------|
|
|
118
|
+
| `OPENROUTER_API_KEY` | OpenRouter (recommended) | `anthropic/claude-sonnet-4-5` |
|
|
119
|
+
| `ANTHROPIC_API_KEY` | Anthropic direct | `claude-sonnet-4-5-20251101` |
|
|
120
|
+
| `OPENAI_API_KEY` | OpenAI | `gpt-4o` |
|
|
121
|
+
| `OPENAI_BASE_URL` | Local (Ollama / LM Studio) | `http://localhost:11434/v1` |
|
|
122
|
+
|
|
123
|
+
### Model Pool (optional — up to 5 models)
|
|
124
|
+
|
|
125
|
+
Configure a named pool. JellyOS rotates through them automatically on rate-limits or errors:
|
|
126
|
+
|
|
127
|
+
```env
|
|
128
|
+
JELLY_MODEL_1=anthropic/claude-sonnet-4-5 # primary
|
|
129
|
+
JELLY_MODEL_2=openai/gpt-4o-mini # secondary
|
|
130
|
+
JELLY_MODEL_3=google/gemini-flash-1.5 # tertiary
|
|
131
|
+
JELLY_MODEL_4=meta-llama/llama-3-8b-instruct:free # budget / eco
|
|
132
|
+
JELLY_MODEL_5=deepseek/deepseek-chat # swarm fallback
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
If not set, JellyOS uses built-in defaults for your provider.
|
|
136
|
+
|
|
137
|
+
### Data & Chain APIs (optional)
|
|
138
|
+
|
|
139
|
+
| Variable | Description |
|
|
140
|
+
|----------|-------------|
|
|
141
|
+
| `ALCHEMY_KEY` | On-chain balances, gas, whale scanning across 16 EVM chains |
|
|
142
|
+
| `COINGLASS_API_KEY` | Funding rates + open interest (broader exchange coverage) |
|
|
143
|
+
| `DUNE_API_KEY` | Dune Analytics whale wallet tracking |
|
|
144
|
+
| `GLASSNODE_API_KEY` | On-chain metrics (active addresses, SOPR) |
|
|
145
|
+
| `POLYMARKET_API_KEY` | Prediction market data and trading |
|
|
146
|
+
|
|
147
|
+
### Vault & Agent Behaviour
|
|
148
|
+
|
|
149
|
+
| Variable | Default | Description |
|
|
150
|
+
|----------|---------|-------------|
|
|
151
|
+
| `JELLY_EFFECT_LEVEL` | `normal` | Default power level: `eco` / `normal` / `turbo` / `max` |
|
|
152
|
+
| `AUTO_VAULT_THRESHOLD` | `500` | Auto-sweep P&L to vault when this USD amount is exceeded |
|
|
153
|
+
| `JELLY_DASHBOARD_PORT` | `4320` | WebSocket port for the local dashboard |
|
|
154
|
+
| `JELLY_MAX_AGENTS` | `5` | Max parallel sub-agents in swarm mode |
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Effect Levels
|
|
159
|
+
|
|
160
|
+
Switch at any time with `/effect <level>`. Takes effect immediately on your next message.
|
|
161
|
+
|
|
162
|
+
| Level | Power | Swarm | Best For |
|
|
163
|
+
|-------|-------|-------|----------|
|
|
164
|
+
| `eco` | 30% | Off | Quick balance checks, simple questions |
|
|
165
|
+
| `normal` | 50% | Off | Default — most everyday tasks |
|
|
166
|
+
| `turbo` | 70% | 2 agents | Analysis + signal generation in parallel |
|
|
167
|
+
| `max` | 100% | 5 agents | Multi-chain deep research, complex strategies |
|
|
168
|
+
|
|
169
|
+
**How swarm works (`turbo` / `max`):**
|
|
170
|
+
Complex prompts are automatically scored using a conjunction + action-verb heuristic. When the score crosses the threshold, the prompt is decomposed into 2–5 focused sub-tasks. Sub-tasks run in groups of 3 (parallel within each group, groups run sequentially). A reviewer model then synthesises all results into a single coherent answer.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Live Data Feeds
|
|
175
|
+
|
|
176
|
+
JellyOS polls 21 sources in the background and injects relevant data into every agent turn:
|
|
177
|
+
|
|
178
|
+
| Source | Interval | Data |
|
|
179
|
+
|--------|----------|------|
|
|
180
|
+
| CoinGecko prices | 5 min | Top asset prices + 24h change |
|
|
181
|
+
| Binance tickers | 2 min | Top 8 USDT pairs by volume |
|
|
182
|
+
| CoinGecko trending | 30 min | 7 trending coins |
|
|
183
|
+
| Fear & Greed Index | 1 hr | Alternative.me sentiment score |
|
|
184
|
+
| DeFiLlama TVL | 1 hr | Total TVL by chain |
|
|
185
|
+
| DeFiLlama protocols | 1 hr | Top TVL movers |
|
|
186
|
+
| Global market cap | 1 hr | BTC dominance, total cap, 24h change |
|
|
187
|
+
| Messari RSS | 10 min | Latest news headlines |
|
|
188
|
+
| CoinTelegraph RSS | 10 min | Latest news headlines |
|
|
189
|
+
| Coinglass funding rates | 15 min | BTC funding rates across exchanges |
|
|
190
|
+
| Coinglass open interest | 15 min | BTC OI aggregate |
|
|
191
|
+
| Etherscan gas | 1 min | ETH base / fast / slow gas |
|
|
192
|
+
| Reddit sentiment | 30 min | r/CryptoCurrency hot posts |
|
|
193
|
+
| Solana TPS | 5 min | Network transactions per second |
|
|
194
|
+
| BTC mempool | 3 min | Pending txs + fee rates |
|
|
195
|
+
| Polymarket trends | 30 min | Trending prediction markets |
|
|
196
|
+
| Whale watch | 10 min | Large on-chain movements (Alchemy) |
|
|
197
|
+
| Dune Analytics | 1 hr | Whale wallet tracker (API key required) |
|
|
198
|
+
| Glassnode | 1 hr | BTC active addresses (API key required) |
|
|
199
|
+
| CryptoCompare social | 2 hr | Twitter / Reddit social stats |
|
|
200
|
+
| Kalshi markets | 30 min | Prediction market odds |
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## REPL Commands
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
/help List all available commands
|
|
208
|
+
/effect [level] Set effect level: eco | normal | turbo | max
|
|
209
|
+
/vault Show encrypted vault balance
|
|
210
|
+
/unlock <passphrase> Unlock the vault for sweeps
|
|
211
|
+
/lock Lock the vault immediately
|
|
212
|
+
/agents Show live swarm routing status
|
|
213
|
+
/feeds Show feed statistics (last fetch, item count)
|
|
214
|
+
/wallets Show all wallet addresses (EVM / Solana / Cosmos)
|
|
215
|
+
/signals Request current trading signals from signal engine
|
|
216
|
+
/status Full system health check (feeds, vault, model, wallets)
|
|
217
|
+
/clear Clear conversation history
|
|
218
|
+
/panic Emergency stop — closes all positions, sweeps vault, locks, halts feeds
|
|
219
|
+
/exit Quit JellyOS
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Tools Reference
|
|
225
|
+
|
|
226
|
+
The AI calls these tools automatically. You can also ask for them in plain language.
|
|
227
|
+
|
|
228
|
+
| Tool | Description |
|
|
229
|
+
|------|-------------|
|
|
230
|
+
| `get_balance` | Wallet balance on any supported chain |
|
|
231
|
+
| `get_wallet_addresses` | All generated wallet addresses |
|
|
232
|
+
| `sign_transaction` | Sign a tx hex in-memory (key never broadcast) |
|
|
233
|
+
| `vault_status` | Vault balance + lock state + entry count |
|
|
234
|
+
| `vault_sweep` | Move profits from trading account into vault |
|
|
235
|
+
| `vault_history` | Last 50 vault entries |
|
|
236
|
+
| `get_live_feeds` | Latest items from all active feed sources |
|
|
237
|
+
| `get_signals` | Active directional signals from the signal engine |
|
|
238
|
+
| `get_fear_greed` | Crypto Fear & Greed Index (current + 7d history) |
|
|
239
|
+
| `get_funding_rates` | Perpetual funding rates across exchanges |
|
|
240
|
+
| `get_market_data` | Prices + 24h change for up to 10 assets |
|
|
241
|
+
| `get_defi_tvl` | DeFiLlama TVL by chain or protocol |
|
|
242
|
+
| `get_gas_prices` | Gas prices across EVM chains |
|
|
243
|
+
| `scan_chain` | Scan recent blocks for large whale transactions |
|
|
244
|
+
| `get_polymarket` | Trending prediction markets + current odds |
|
|
245
|
+
| `predict_market` | AI-generated price prediction for an asset |
|
|
246
|
+
| `execute_trade` | Submit a swap (requires explicit user confirmation) |
|
|
247
|
+
| `get_positions` | List all open positions |
|
|
248
|
+
| `get_portfolio` | Full portfolio summary with P&L |
|
|
249
|
+
| `calculate_risk` | Risk/reward ratio + recommended position size |
|
|
250
|
+
| `set_stop_loss` | Update a stop-loss level |
|
|
251
|
+
| `execute_skill` | Run a saved trading strategy |
|
|
252
|
+
| `list_skills` | Show available strategy skills |
|
|
253
|
+
| `get_system_status` | Health check for all agent subsystems |
|
|
254
|
+
| `get_context` | Read persistent session context |
|
|
255
|
+
| `set_context` | Write persistent session context |
|
|
256
|
+
| `get_news` | Latest crypto news headlines |
|
|
257
|
+
| `get_chain_list` | All supported chains with IDs |
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Supported Chains
|
|
262
|
+
|
|
263
|
+
Ethereum, BSC, Arbitrum, Base, Polygon, Avalanche, Optimism, Scroll, Linea, zkSync Era, Mantle, Blast, Solana, Cosmos, and more via Alchemy and public RPCs.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Local Dashboard
|
|
268
|
+
|
|
269
|
+
An optional React dashboard connects to the agent over WebSocket and shows live data:
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
cd dashboard
|
|
273
|
+
npm install
|
|
274
|
+
npm run dev
|
|
275
|
+
# Open http://localhost:4321
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Real-time events pushed from the agent:
|
|
279
|
+
|
|
280
|
+
| Event | Description |
|
|
281
|
+
|-------|-------------|
|
|
282
|
+
| `feed_item` | New item from any live feed |
|
|
283
|
+
| `log_entry` | Agent / tool conversation messages |
|
|
284
|
+
| `trade_executed` | A trade was submitted |
|
|
285
|
+
| `vault_sweep` | Auto-vault swept profits |
|
|
286
|
+
| `swarm_update` | Sub-agent start / complete events |
|
|
287
|
+
| `signal_update` | New trading signal generated |
|
|
288
|
+
| `vault_update` | Vault balance changed |
|
|
289
|
+
| `panic` | Emergency stop triggered |
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Extension API
|
|
294
|
+
|
|
295
|
+
Build your own tools, commands, and hooks that run inside the JellyOS agent loop:
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
import { Type } from "@jellyos/agent";
|
|
299
|
+
import type { ExtensionAPI } from "@jellyos/agent";
|
|
300
|
+
|
|
301
|
+
export default function (agent: ExtensionAPI) {
|
|
302
|
+
// Set the AI's system prompt
|
|
303
|
+
agent.setSystemPrompt("You are a DeFi yield optimizer.");
|
|
304
|
+
|
|
305
|
+
// Register a slash command
|
|
306
|
+
agent.registerCommand("yields", {
|
|
307
|
+
description: "Show top yield opportunities",
|
|
308
|
+
async handler(_args, ctx) {
|
|
309
|
+
ctx.ui.notify("Fetching yields...");
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// Register a tool the AI can call automatically
|
|
314
|
+
agent.registerTool({
|
|
315
|
+
name: "get_apy",
|
|
316
|
+
label: "Get APY",
|
|
317
|
+
description: "Fetch current APY for a DeFi protocol",
|
|
318
|
+
parameters: Type.Object({
|
|
319
|
+
protocol: Type.String({ description: "Protocol name, e.g. aave" }),
|
|
320
|
+
}),
|
|
321
|
+
async execute(_id, { protocol }) {
|
|
322
|
+
const res = await fetch(`https://api.llama.fi/protocol/${protocol}`);
|
|
323
|
+
const data = await res.json() as any;
|
|
324
|
+
return {
|
|
325
|
+
content: [{ type: "text", text: `APY data for ${protocol}: ${JSON.stringify(data.apy)}` }],
|
|
326
|
+
details: {},
|
|
327
|
+
};
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// Lifecycle hooks
|
|
332
|
+
agent.on("session_start", async (ctx) => {
|
|
333
|
+
ctx.ui.setStatus("yields", "ready");
|
|
334
|
+
ctx.ui.notify("Yield optimizer loaded.");
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
agent.on("session_end", async () => {
|
|
338
|
+
// cleanup
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Load your extension at startup:
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
jellyos --extension ./my-extension.ts
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## CLI Reference
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
jellyos # interactive TUI (auto-loads extensions/)
|
|
355
|
+
jellyos --extension /path/to/ext.ts # load a specific extension file
|
|
356
|
+
jellyos --prompt /path/to/system.md # override system prompt from file
|
|
357
|
+
jellyos config # show current config (keys masked)
|
|
358
|
+
jellyos setup # run the setup wizard
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## Security
|
|
364
|
+
|
|
365
|
+
- **Keys stay local** — API keys are read from `~/.jellyos/.env` at startup and never logged or transmitted beyond outbound API calls
|
|
366
|
+
- **Private keys never leave the process** — signing happens in memory; only the resulting signature is returned
|
|
367
|
+
- **Vault encryption** — AES-256-GCM with a key derived from your passphrase using scrypt (memory-hard KDF) + random per-vault salt; the key itself is never persisted
|
|
368
|
+
- **Auto-lock** — vault locks automatically on `/panic` and on process exit
|
|
369
|
+
- **Wallet storage** — keypairs are written to `~/.jellyos/wallets/` which is in `.gitignore` and never included in any sync or backup by the agent
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## License
|
|
374
|
+
|
|
375
|
+
MIT — see [LICENSE](LICENSE)
|
package/bin/jellyagent
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* jellyagent / jellyos — CLI entry point.
|
|
4
|
+
* Uses tsx for transparent TypeScript support at runtime.
|
|
5
|
+
* No compilation step needed when running from source.
|
|
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
|
+
// Prefer pre-built dist/ if available, otherwise run TypeScript source via tsx
|
|
16
|
+
const distEntry = join(__dirname, "..", "dist", "cli.js");
|
|
17
|
+
const srcEntry = join(__dirname, "..", "src", "cli.ts");
|
|
18
|
+
|
|
19
|
+
if (existsSync(distEntry)) {
|
|
20
|
+
// Built package — just run the compiled JS
|
|
21
|
+
await import(distEntry);
|
|
22
|
+
} else if (existsSync(srcEntry)) {
|
|
23
|
+
// Development / source install — run via tsx
|
|
24
|
+
const { register } = await import("tsx/esm");
|
|
25
|
+
// tsx registers TypeScript support on the module system
|
|
26
|
+
await import(srcEntry);
|
|
27
|
+
} else {
|
|
28
|
+
console.error("JellyOS: could not find dist/cli.js or src/cli.ts");
|
|
29
|
+
console.error("Run: npm run build or pnpm build");
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ExtensionAPI — the interface exposed to extension files (e.g. extensions/jellyos.ts).
|
|
3
|
+
*
|
|
4
|
+
* Drop-in replacement for Pi's ExtensionAPI. Extension files that ran under Pi
|
|
5
|
+
* work without changes — all Pi methods and calling conventions are preserved:
|
|
6
|
+
* - ui.setStatus, ui.setTheme, ui.setHeader
|
|
7
|
+
* - ctx.hasUI
|
|
8
|
+
* - pi.on("session_start", async (_event, ctx) => { ... }) ← Pi convention
|
|
9
|
+
* - pi.on("session_shutdown", async () => { ... })
|
|
10
|
+
* - pi.on("before_agent_start", async (_event, ctx) => { ... })
|
|
11
|
+
*/
|
|
12
|
+
import { type Static, type TObject, type TSchema } from "@sinclair/typebox";
|
|
13
|
+
export type { Static, TObject, TSchema };
|
|
14
|
+
export interface ToolContent {
|
|
15
|
+
content: Array<{
|
|
16
|
+
type: "text";
|
|
17
|
+
text: string;
|
|
18
|
+
}>;
|
|
19
|
+
details: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
export declare function text(t: string): ToolContent;
|
|
22
|
+
export interface ToolDef<P extends TSchema = TSchema> {
|
|
23
|
+
name: string;
|
|
24
|
+
label: string;
|
|
25
|
+
description: string;
|
|
26
|
+
parameters: P;
|
|
27
|
+
execute(id: string, params: Static<P>): Promise<ToolContent>;
|
|
28
|
+
}
|
|
29
|
+
export interface TuiHeader {
|
|
30
|
+
render(width: number): string[];
|
|
31
|
+
invalidate(): void;
|
|
32
|
+
}
|
|
33
|
+
export type HeaderFactory = (tui: unknown, theme: ThemeContext) => TuiHeader;
|
|
34
|
+
export interface ThemeContext {
|
|
35
|
+
/** Apply a named colour to text. Colours: accent, error, warn, muted, success, border, dim */
|
|
36
|
+
fg(color: string, text: string): string;
|
|
37
|
+
}
|
|
38
|
+
export interface UIContext {
|
|
39
|
+
/** Show a bordered notification in the REPL panel */
|
|
40
|
+
notify(message: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* Set a named status badge in the status bar.
|
|
43
|
+
* Pi compat — multiple badges can be shown simultaneously.
|
|
44
|
+
* @param key Badge slot name (e.g. "vault", "jelly", "chain")
|
|
45
|
+
* @param value Themed text to display
|
|
46
|
+
*/
|
|
47
|
+
setStatus(key: string, value: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* Activate a named theme. Pi compat — engine uses jelly theme by default.
|
|
50
|
+
*/
|
|
51
|
+
setTheme(name: string): void;
|
|
52
|
+
/**
|
|
53
|
+
* Replace the TUI header with a custom rendering factory.
|
|
54
|
+
* Pi compat — accepted but engine renders its own Ink header.
|
|
55
|
+
*/
|
|
56
|
+
setHeader(factory: HeaderFactory): void;
|
|
57
|
+
theme: ThemeContext;
|
|
58
|
+
}
|
|
59
|
+
export interface CommandContext {
|
|
60
|
+
ui: UIContext;
|
|
61
|
+
}
|
|
62
|
+
export interface CommandDef {
|
|
63
|
+
description: string;
|
|
64
|
+
handler(args: string, ctx: CommandContext): Promise<void>;
|
|
65
|
+
}
|
|
66
|
+
export interface SkillDef {
|
|
67
|
+
name: string;
|
|
68
|
+
content: string;
|
|
69
|
+
}
|
|
70
|
+
export type SessionEvent = "session_start" | "session_end" | "session_shutdown" | "before_agent_start";
|
|
71
|
+
export interface SessionContext {
|
|
72
|
+
ui: UIContext;
|
|
73
|
+
/** True when running inside an interactive TUI (always true for @jellychain/agent) */
|
|
74
|
+
hasUI: boolean;
|
|
75
|
+
config: Record<string, string | undefined>;
|
|
76
|
+
}
|
|
77
|
+
export interface ExtensionAPI {
|
|
78
|
+
registerCommand(name: string, def: CommandDef): void;
|
|
79
|
+
registerTool<P extends TSchema>(def: ToolDef<P>): void;
|
|
80
|
+
registerSkill(def: SkillDef): void;
|
|
81
|
+
/**
|
|
82
|
+
* Subscribe to a lifecycle event.
|
|
83
|
+
* Pi calling convention: handler receives (event, ctx).
|
|
84
|
+
* Handlers may use any arity: (event, ctx) | (ctx) | () — all are safe.
|
|
85
|
+
*/
|
|
86
|
+
on(event: SessionEvent, handler: (...args: any[]) => Promise<void>): void;
|
|
87
|
+
setSystemPrompt(prompt: string): void;
|
|
88
|
+
/** Direct UI access available at any time */
|
|
89
|
+
ui: UIContext;
|
|
90
|
+
}
|
|
91
|
+
export type ExtensionModule = (api: ExtensionAPI) => void | Promise<void>;
|
|
92
|
+
//# sourceMappingURL=ExtensionAPI.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExtensionAPI.d.ts","sourceRoot":"","sources":["../../src/api/ExtensionAPI.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5E,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAIzC,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,CAE3C;AAID,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO;IAClD,IAAI,EAAS,MAAM,CAAC;IACpB,KAAK,EAAQ,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAG,CAAC,CAAC;IACf,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CAC9D;AAID,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAChC,UAAU,IAAI,IAAI,CAAC;CACpB;AACD,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,KAAK,SAAS,CAAC;AAI7E,MAAM,WAAW,YAAY;IAC3B,8FAA8F;IAC9F,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACzC;AAED,MAAM,WAAW,SAAS;IACxB,qDAAqD;IACrD,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAE9B;;;;;OAKG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5C;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAE7B;;;OAGG;IACH,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;IAExC,KAAK,EAAE,YAAY,CAAC;CACrB;AAID,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,SAAS,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3D;AAID,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAK,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAID,MAAM,MAAM,YAAY,GACpB,eAAe,GACf,aAAa,GACb,kBAAkB,GAClB,oBAAoB,CAAC;AAEzB,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAK,SAAS,CAAC;IACjB,sFAAsF;IACtF,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC5C;AAID,MAAM,WAAW,YAAY;IAC3B,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IACrD,YAAY,CAAC,CAAC,SAAS,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACvD,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAAC;IAEnC;;;;OAIG;IACH,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAE1E,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAEtC,6CAA6C;IAC7C,EAAE,EAAE,SAAS,CAAC;CACf;AAID,MAAM,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ExtensionAPI — the interface exposed to extension files (e.g. extensions/jellyos.ts).
|
|
3
|
+
*
|
|
4
|
+
* Drop-in replacement for Pi's ExtensionAPI. Extension files that ran under Pi
|
|
5
|
+
* work without changes — all Pi methods and calling conventions are preserved:
|
|
6
|
+
* - ui.setStatus, ui.setTheme, ui.setHeader
|
|
7
|
+
* - ctx.hasUI
|
|
8
|
+
* - pi.on("session_start", async (_event, ctx) => { ... }) ← Pi convention
|
|
9
|
+
* - pi.on("session_shutdown", async () => { ... })
|
|
10
|
+
* - pi.on("before_agent_start", async (_event, ctx) => { ... })
|
|
11
|
+
*/
|
|
12
|
+
export function text(t) {
|
|
13
|
+
return { content: [{ type: "text", text: t }], details: {} };
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=ExtensionAPI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExtensionAPI.js","sourceRoot":"","sources":["../../src/api/ExtensionAPI.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAaH,MAAM,UAAU,IAAI,CAAC,CAAS;IAC5B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AAC/D,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry — stores all commands, tools, and skills registered by extension files.
|
|
3
|
+
*
|
|
4
|
+
* Pi calling convention for lifecycle hooks:
|
|
5
|
+
* session_start / session_end / session_shutdown: handler(event, ctx)
|
|
6
|
+
* before_agent_start: handler(event, ctx)
|
|
7
|
+
*
|
|
8
|
+
* We always call hooks as handler(undefined, ctx) — handlers that expect only
|
|
9
|
+
* one argument (ctx) will receive it as the first positional parameter only if
|
|
10
|
+
* they follow Pi's two-arg (event, ctx) convention; handlers with no args are
|
|
11
|
+
* also safe. This matches Pi's actual calling convention exactly.
|
|
12
|
+
*/
|
|
13
|
+
import type { CommandDef, ToolDef, SkillDef, SessionEvent, SessionContext } from "./ExtensionAPI.js";
|
|
14
|
+
import type { TSchema } from "@sinclair/typebox";
|
|
15
|
+
export declare class Registry {
|
|
16
|
+
private commands;
|
|
17
|
+
private tools;
|
|
18
|
+
private skills;
|
|
19
|
+
private hooks;
|
|
20
|
+
private _systemPrompt;
|
|
21
|
+
addCommand(name: string, def: CommandDef): void;
|
|
22
|
+
addTool<P extends TSchema>(def: ToolDef<P>): void;
|
|
23
|
+
addSkill(def: SkillDef): void;
|
|
24
|
+
addHook(event: SessionEvent, handler: (...args: any[]) => Promise<void>): void;
|
|
25
|
+
setSystemPrompt(prompt: string): void;
|
|
26
|
+
getCommand(name: string): CommandDef | undefined;
|
|
27
|
+
listCommands(): Array<[string, CommandDef]>;
|
|
28
|
+
listTools(): Array<ToolDef<TSchema>>;
|
|
29
|
+
getTool(name: string): ToolDef<TSchema> | undefined;
|
|
30
|
+
listSkills(): SkillDef[];
|
|
31
|
+
getSystemPrompt(): string;
|
|
32
|
+
/**
|
|
33
|
+
* Fire hooks for a lifecycle event.
|
|
34
|
+
*
|
|
35
|
+
* Supports all handler arities so Pi-compat and simplified extensions both work:
|
|
36
|
+
* h.length === 0 : async () => { ... } → called with no args
|
|
37
|
+
* h.length === 1 : async (ctx) => { ... } → called with ctx as first arg
|
|
38
|
+
* h.length >= 2 : async (event, ctx) => { ... } → Pi convention: (undefined, ctx)
|
|
39
|
+
*
|
|
40
|
+
* Using Function.length (declared parameter count) lets us detect intent without
|
|
41
|
+
* runtime type checks, avoiding the bug where h(undefined, ctx) puts `undefined`
|
|
42
|
+
* into the `ctx` parameter of a single-argument handler.
|
|
43
|
+
*/
|
|
44
|
+
fireHook(event: string, ctx: SessionContext): Promise<void>;
|
|
45
|
+
toOpenAITools(): Array<{
|
|
46
|
+
type: "function";
|
|
47
|
+
function: {
|
|
48
|
+
name: string;
|
|
49
|
+
description: string;
|
|
50
|
+
parameters: unknown;
|
|
51
|
+
};
|
|
52
|
+
}>;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=Registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Registry.d.ts","sourceRoot":"","sources":["../../src/api/Registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EACV,UAAU,EAAE,OAAO,EAAE,QAAQ,EAC7B,YAAY,EAAE,cAAc,EAC7B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAMjD,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,MAAM,CAAmB;IAEjC,OAAO,CAAC,KAAK,CAAgC;IAE7C,OAAO,CAAC,aAAa,CAAM;IAI3B,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI;IAI/C,OAAO,CAAC,CAAC,SAAS,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;IAIjD,QAAQ,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI;IAI7B,OAAO,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAQ9E,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAMrC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAIhD,YAAY,IAAI,KAAK,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAI3C,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAIpC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,SAAS;IAInD,UAAU,IAAI,QAAQ,EAAE;IAIxB,eAAe,IAAI,MAAM;IAMzB;;;;;;;;;;;OAWG;IACG,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBjE,aAAa,IAAI,KAAK,CAAC;QACrB,IAAI,EAAE,UAAU,CAAC;QACjB,QAAQ,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,OAAO,CAAA;SAAE,CAAC;KACtE,CAAC;CAUH"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry — stores all commands, tools, and skills registered by extension files.
|
|
3
|
+
*
|
|
4
|
+
* Pi calling convention for lifecycle hooks:
|
|
5
|
+
* session_start / session_end / session_shutdown: handler(event, ctx)
|
|
6
|
+
* before_agent_start: handler(event, ctx)
|
|
7
|
+
*
|
|
8
|
+
* We always call hooks as handler(undefined, ctx) — handlers that expect only
|
|
9
|
+
* one argument (ctx) will receive it as the first positional parameter only if
|
|
10
|
+
* they follow Pi's two-arg (event, ctx) convention; handlers with no args are
|
|
11
|
+
* also safe. This matches Pi's actual calling convention exactly.
|
|
12
|
+
*/
|
|
13
|
+
export class Registry {
|
|
14
|
+
commands = new Map();
|
|
15
|
+
tools = new Map();
|
|
16
|
+
skills = [];
|
|
17
|
+
hooks = new Map();
|
|
18
|
+
_systemPrompt = "";
|
|
19
|
+
// ── Registration ────────────────────────────────────────────────────────
|
|
20
|
+
addCommand(name, def) {
|
|
21
|
+
this.commands.set(name.toLowerCase(), def);
|
|
22
|
+
}
|
|
23
|
+
addTool(def) {
|
|
24
|
+
this.tools.set(def.name, def);
|
|
25
|
+
}
|
|
26
|
+
addSkill(def) {
|
|
27
|
+
this.skills.push(def);
|
|
28
|
+
}
|
|
29
|
+
addHook(event, handler) {
|
|
30
|
+
// Normalize aliases: session_shutdown → session_end
|
|
31
|
+
const key = event === "session_shutdown" ? "session_end" : event;
|
|
32
|
+
const list = this.hooks.get(key) ?? [];
|
|
33
|
+
list.push(handler);
|
|
34
|
+
this.hooks.set(key, list);
|
|
35
|
+
}
|
|
36
|
+
setSystemPrompt(prompt) {
|
|
37
|
+
this._systemPrompt = prompt;
|
|
38
|
+
}
|
|
39
|
+
// ── Reads ────────────────────────────────────────────────────────────────
|
|
40
|
+
getCommand(name) {
|
|
41
|
+
return this.commands.get(name.toLowerCase());
|
|
42
|
+
}
|
|
43
|
+
listCommands() {
|
|
44
|
+
return [...this.commands.entries()];
|
|
45
|
+
}
|
|
46
|
+
listTools() {
|
|
47
|
+
return [...this.tools.values()];
|
|
48
|
+
}
|
|
49
|
+
getTool(name) {
|
|
50
|
+
return this.tools.get(name);
|
|
51
|
+
}
|
|
52
|
+
listSkills() {
|
|
53
|
+
return this.skills;
|
|
54
|
+
}
|
|
55
|
+
getSystemPrompt() {
|
|
56
|
+
return this._systemPrompt;
|
|
57
|
+
}
|
|
58
|
+
// ── Hook dispatch ────────────────────────────────────────────────────────
|
|
59
|
+
/**
|
|
60
|
+
* Fire hooks for a lifecycle event.
|
|
61
|
+
*
|
|
62
|
+
* Supports all handler arities so Pi-compat and simplified extensions both work:
|
|
63
|
+
* h.length === 0 : async () => { ... } → called with no args
|
|
64
|
+
* h.length === 1 : async (ctx) => { ... } → called with ctx as first arg
|
|
65
|
+
* h.length >= 2 : async (event, ctx) => { ... } → Pi convention: (undefined, ctx)
|
|
66
|
+
*
|
|
67
|
+
* Using Function.length (declared parameter count) lets us detect intent without
|
|
68
|
+
* runtime type checks, avoiding the bug where h(undefined, ctx) puts `undefined`
|
|
69
|
+
* into the `ctx` parameter of a single-argument handler.
|
|
70
|
+
*/
|
|
71
|
+
async fireHook(event, ctx) {
|
|
72
|
+
const key = event === "session_shutdown" ? "session_end" : event;
|
|
73
|
+
const handlers = this.hooks.get(key) ?? [];
|
|
74
|
+
for (const h of handlers) {
|
|
75
|
+
try {
|
|
76
|
+
if (h.length === 0) {
|
|
77
|
+
await h(); // () => {...}
|
|
78
|
+
}
|
|
79
|
+
else if (h.length === 1) {
|
|
80
|
+
await h(ctx); // (ctx) => {...}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
await h(undefined, ctx); // (_event, ctx) => {...} — Pi convention
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch { /* hooks must not crash the session */ }
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// ── OpenAI tool schema ───────────────────────────────────────────────────
|
|
90
|
+
toOpenAITools() {
|
|
91
|
+
return [...this.tools.values()].map(t => ({
|
|
92
|
+
type: "function",
|
|
93
|
+
function: {
|
|
94
|
+
name: t.name,
|
|
95
|
+
description: t.description,
|
|
96
|
+
parameters: t.parameters,
|
|
97
|
+
},
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=Registry.js.map
|