@quantish/agent 0.1.22 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +246 -162
  2. package/dist/index.js +139 -154
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,116 +1,105 @@
1
- # @quantish/cli
1
+ # @quantish/agent
2
2
 
3
- AI-powered CLI agent for building trading bots on Polymarket.
3
+ AI-powered coding & trading agent for Polymarket. Build trading bots, analyze markets, and execute trades using natural language.
4
4
 
5
- Combines **coding tools** (file system, shell, git) with **trading tools** (Polymarket orders, positions, wallet) powered by Claude AI.
5
+ ## Features
6
6
 
7
- ## How It Works
8
-
9
- Quantish CLI connects to the **Quantish Signing Server** to execute trades on Polymarket. Here's why:
10
-
11
- ### Why We Use a Signing Server
12
-
13
- Polymarket uses a **gasless relayer system** - this means:
14
- - ✅ **Free wallet creation** - No MATIC needed to set up
15
- - ✅ **Free trading** - Polymarket covers gas fees on all transactions
16
- - ✅ **Simplified signing** - Our server handles the complex signature formats
17
-
18
- To make this work reliably, the Quantish Signing Server:
19
- 1. **Handles wallet creation** - Your wallet is created and managed through our server
20
- 2. **Signs transactions** - Orders are signed using Polymarket's required format
21
- 3. **Relays to Polymarket** - Transactions go through Polymarket's official relayer
22
- 4. **Bypasses geo-restrictions** - Our server is hosted in a compatible region
23
-
24
- ### What This Means for You
25
-
26
- - **Your funds are secure** - Only you can authorize transactions via your API key
27
- - **Wallets are non-custodial** - You can export your private key anytime with `export_private_key`
28
- - **Trading is free** - No gas fees, ever
29
- - **It just works** - No VPN or complex setup needed
30
-
31
- > 🔒 **Security Note**: Your private keys are stored encrypted. You can export them and migrate to a self-hosted solution in the future if needed.
7
+ - **🤖 Multi-Provider AI** - Use Anthropic Claude or 100+ OpenRouter models (GLM-4.7, MiniMax, DeepSeek, etc.)
8
+ - **💹 Live Trading** - Place orders, manage positions, check balances on Polymarket
9
+ - **🔧 Full Coding Tools** - Read/write files, run commands, git operations
10
+ - **🌐 Web Search** - Search the web with Exa AI or DuckDuckGo fallback
11
+ - **💾 Session Persistence** - Save and resume conversations across sessions
12
+ - **⚡ Queued Input** - Type while the agent is working, queue messages
13
+ - **📊 Cost Tracking** - Real-time token usage and cost display
32
14
 
33
15
  ## Installation
34
16
 
35
17
  ```bash
36
- npm install -g @quantish/cli
37
- ```
38
-
39
- Or run directly with npx:
40
-
41
- ```bash
42
- npx @quantish/cli
18
+ npm install -g @quantish/agent
43
19
  ```
44
20
 
45
21
  ## Quick Start
46
22
 
47
- ### 1. Initialize
48
-
49
- Set up your API keys:
50
-
51
23
  ```bash
24
+ # First-time setup
52
25
  quantish init
53
- ```
54
-
55
- You'll need:
56
- - **Anthropic API Key** - Get one at https://console.anthropic.com/
57
- - **Quantish API Key** - Created automatically during setup
58
-
59
- ### 2. Start Building
60
26
 
61
- **Interactive mode:**
62
-
63
- ```bash
27
+ # Start interactive chat
64
28
  quantish
65
29
  ```
66
30
 
67
- Example conversations:
31
+ ## How It Works
68
32
 
69
- ```
70
- You: Create a trading bot that monitors my positions and sells when profit > 20%
71
- Assistant: I'll create that for you. Let me first check your current positions...
72
- [Calling get_positions...]
73
- [Writing bot.ts...]
33
+ The agent connects to two MCP (Model Context Protocol) servers:
74
34
 
75
- You: What's my current balance?
76
- Assistant: Your Safe wallet has 0.68 USDC available for trading.
35
+ 1. **Discovery MCP** (Public) - Market search, trending markets, market details
36
+ 2. **Trading MCP** (Your API Key) - Wallet, orders, positions, trades
77
37
 
78
- You: Place a $5 YES order on Trump winning at 55 cents
79
- Assistant: Order placed! Order ID: abc123...
80
- ```
38
+ Your wallet is created and managed through our signing server, which:
39
+ - Handles gasless transactions (Polymarket covers fees)
40
+ - ✅ Signs orders using Polymarket's required format
41
+ - ✅ Works globally (no geo-restrictions)
42
+ - 🔒 Non-custodial - export your private key anytime
81
43
 
82
- **One-shot mode:**
44
+ ## Interactive Commands
83
45
 
84
- ```bash
85
- quantish -p "check my open orders"
86
- ```
46
+ ### Chat Commands
47
+
48
+ | Command | Description |
49
+ |---------|-------------|
50
+ | `/help` | Show all commands |
51
+ | `/clear` | Clear conversation history |
52
+ | `/compact` | Summarize conversation to save tokens |
53
+ | `/model <name>` | Switch model (opus, sonnet, haiku, glm, minimax, etc.) |
54
+ | `/provider <name>` | Switch LLM provider (anthropic, openrouter) |
55
+ | `/cost` | Show session cost breakdown |
56
+ | `/tools` | List available tools |
57
+ | `/config` | Show configuration info |
87
58
 
88
- **Piped input:**
59
+ ### Session Commands
89
60
 
90
- ```bash
91
- echo "show my positions" | quantish
92
- ```
61
+ | Command | Description |
62
+ |---------|-------------|
63
+ | `/save [name]` | Save current session |
64
+ | `/resume` | Resume last session |
65
+ | `/sessions` | List all saved sessions |
66
+ | `/load <id>` | Load a session by ID |
67
+ | `/forget` | Delete all saved sessions |
93
68
 
94
- ## Commands
69
+ ### Process Commands
95
70
 
96
71
  | Command | Description |
97
72
  |---------|-------------|
98
- | `quantish` | Start interactive chat |
99
- | `quantish init` | Configure API keys |
100
- | `quantish config` | View configuration |
101
- | `quantish tools` | List available tools |
102
- | `quantish -p "..."` | Run one-shot prompt |
73
+ | `/processes` | List running background processes |
74
+ | `/stop <id>` | Stop a background process |
75
+ | `/stopall` | Stop all background processes |
76
+
77
+ ### Keyboard Shortcuts
78
+
79
+ | Key | Action |
80
+ |-----|--------|
81
+ | `Enter` | Send message (or queue if agent is working) |
82
+ | `Esc` | Interrupt current generation |
83
+ | `Ctrl+C` | Exit CLI |
103
84
 
104
- ## Options
85
+ ## CLI Options
86
+
87
+ ```bash
88
+ quantish # Interactive mode
89
+ quantish init # First-time setup wizard
90
+ quantish config # View configuration
91
+ quantish config --export # Export as .env format
92
+ quantish tools # List all available tools
93
+ quantish -p "message" # One-shot mode
94
+ quantish --version # Show version
95
+ ```
105
96
 
106
97
  | Option | Description |
107
98
  |--------|-------------|
108
99
  | `-p, --prompt <message>` | Run a single prompt |
109
- | `-v, --verbose` | Show tool calls |
100
+ | `-v, --verbose` | Show detailed tool calls |
110
101
  | `--no-mcp` | Disable trading tools |
111
102
  | `--no-local` | Disable coding tools |
112
- | `--version` | Show version |
113
- | `--help` | Show help |
114
103
 
115
104
  ## Available Tools
116
105
 
@@ -118,118 +107,166 @@ echo "show my positions" | quantish
118
107
 
119
108
  | Tool | Description |
120
109
  |------|-------------|
121
- | `read_file` | Read file contents |
122
- | `write_file` | Write/create files |
110
+ | `read_file` | Read file contents with line numbers |
111
+ | `write_file` | Create or overwrite files |
112
+ | `edit_file` | Search and replace in files |
113
+ | `edit_lines` | Edit specific line ranges (efficient) |
123
114
  | `list_dir` | List directory contents |
124
115
  | `delete_file` | Delete files |
125
116
  | `file_exists` | Check if file exists |
126
- | `run_command` | Execute shell commands |
117
+ | `run_command` | Execute shell commands (blocking) |
118
+ | `start_background_process` | Run long-running processes |
119
+ | `get_process_output` | Get output from background process |
120
+ | `stop_process` | Stop a background process |
127
121
  | `grep` | Search file contents |
128
122
  | `find_files` | Find files by pattern |
129
- | `git_status` | Get git status |
130
- | `git_diff` | Show git diff |
123
+ | `setup_env` | Create/update .env files |
124
+
125
+ ### Git Tools
126
+
127
+ | Tool | Description |
128
+ |------|-------------|
129
+ | `git_status` | Get repository status |
130
+ | `git_diff` | Show changes |
131
131
  | `git_add` | Stage files |
132
132
  | `git_commit` | Create commits |
133
133
  | `git_log` | Show commit history |
134
134
  | `git_checkout` | Switch branches |
135
- | `web_search` | Search the web (Exa/DuckDuckGo) |
136
- | `web_answer` | AI-powered Q&A (Exa) |
137
- | `fetch_url` | Fetch URL content |
138
135
 
139
- ### MCP Tools (Trading)
136
+ ### Web Tools
140
137
 
141
138
  | Tool | Description |
142
139
  |------|-------------|
143
- | `get_balances` | Check wallet balances |
144
- | `get_positions` | View current positions |
145
- | `place_order` | Place buy/sell orders |
146
- | `cancel_order` | Cancel open orders |
147
- | `get_orders` | List orders |
148
- | `get_orderbook` | Get market orderbook |
149
- | `get_price` | Get current price |
150
- | `transfer_usdc` | Transfer USDC |
151
- | `swap_tokens` | Swap tokens |
152
- | `claim_winnings` | Claim from resolved markets |
153
-
154
- ## Configuration
140
+ | `web_search` | Search the web (Exa/DuckDuckGo) |
141
+ | `web_answer` | AI-powered Q&A (requires Exa API key) |
142
+ | `fetch_url` | Fetch URL content |
155
143
 
156
- Configuration is stored in `~/.quantish/config.json`:
144
+ ### MCP Tools (Trading)
157
145
 
158
- ```json
159
- {
160
- "anthropicApiKey": "sk-ant-...",
161
- "quantishApiKey": "pk_live_...",
162
- "mcpServerUrl": "https://quantish-sdk-production.up.railway.app/mcp"
163
- }
164
- ```
146
+ | Tool | Server | Description |
147
+ |------|--------|-------------|
148
+ | `search_markets` | Discovery | Search markets by query |
149
+ | `get_trending_markets` | Discovery | Get trending/popular markets |
150
+ | `get_market_details` | Discovery | Get market info and prices |
151
+ | `get_balances` | Trading | Check wallet balances |
152
+ | `get_positions` | Trading | View current positions |
153
+ | `place_order` | Trading | Place buy/sell orders |
154
+ | `cancel_order` | Trading | Cancel open orders |
155
+ | `get_orders` | Trading | List orders |
156
+ | `get_orderbook` | Trading | Get market orderbook |
157
+ | `get_price` | Trading | Get current price |
158
+ | `transfer_usdc` | Trading | Transfer USDC |
159
+ | `claim_winnings` | Trading | Claim from resolved markets |
160
+ | `export_private_key` | Trading | Export wallet private key |
165
161
 
166
- Environment variables take precedence:
167
- - `ANTHROPIC_API_KEY`
168
- - `QUANTISH_API_KEY`
162
+ ## LLM Providers
169
163
 
170
- ## Examples
164
+ ### Anthropic (Default for new installs)
171
165
 
172
- ### Build a Trading Bot
166
+ Uses Claude models directly via Anthropic API.
173
167
 
174
168
  ```bash
175
- quantish
176
- > Create a Python script that monitors the Trump market and alerts me when price drops below 40 cents
169
+ /model opus # Claude Opus 4.5 - Most capable
170
+ /model sonnet # Claude Sonnet 4.5 - Balanced (default)
171
+ /model haiku # Claude Haiku 4.5 - Fastest/cheapest
177
172
  ```
178
173
 
179
- ### Manage Positions
180
-
181
- ```bash
182
- quantish -p "show me my positions with unrealized P&L"
183
- ```
174
+ ### OpenRouter
184
175
 
185
- ### Market Making
176
+ Access 100+ models from various providers.
186
177
 
187
178
  ```bash
188
- quantish
189
- > Help me set up a basic market making strategy. I want to place both bid and ask orders around the current mid price.
190
- ```
179
+ /provider openrouter # Switch to OpenRouter
191
180
 
192
- ### Code Review
181
+ /model glm # GLM-4.7 (default for OpenRouter) - Best for coding
182
+ /model minimax # MiniMax M2.1 - Fast and cheap
183
+ /model deepseek # DeepSeek V3.2 - Great reasoning
184
+ /model gemini # Gemini 2.0 Flash - Google's latest
185
+ /model grok # Grok 3 Mini Beta - xAI
186
+ ```
193
187
 
188
+ Or use any OpenRouter model ID:
194
189
  ```bash
195
- quantish
196
- > Read my trading bot code in bot.ts and suggest improvements
190
+ /model anthropic/claude-3.5-sonnet
191
+ /model meta-llama/llama-3.3-70b-instruct
197
192
  ```
198
193
 
199
- ## Development
194
+ ## Configuration
200
195
 
201
- ```bash
202
- # Clone the repo
203
- git clone https://github.com/quantish/cli
196
+ Configuration is stored in `~/.quantish/config.json`.
204
197
 
205
- # Install dependencies
206
- cd packages/quantish-cli
207
- npm install
198
+ ### Environment Variables
208
199
 
209
- # Build
210
- npm run build
200
+ | Variable | Description |
201
+ |----------|-------------|
202
+ | `ANTHROPIC_API_KEY` | Anthropic API key |
203
+ | `OPENROUTER_API_KEY` | OpenRouter API key |
204
+ | `QUANTISH_API_KEY` | Quantish trading API key |
205
+ | `EXA_API_KEY` | Exa AI search key (optional) |
206
+ | `MCP_SERVER_URL` | Custom Trading MCP server URL |
211
207
 
212
- # Run locally
213
- npm start
208
+ ### Export Configuration
214
209
 
215
- # Development mode (watch)
216
- npm run dev
210
+ ```bash
211
+ quantish config --export > .env
217
212
  ```
218
213
 
219
- ## Architecture
220
-
221
- ```
222
- quantish (CLI)
223
-
224
- ├── Local Tools (filesystem, shell, git)
225
- │ └── Runs directly on your machine
226
-
227
- └── MCP Tools (trading)
228
- └── Calls Quantish MCP Server
229
- └── Executes on Polymarket
214
+ ## Building Applications
215
+
216
+ The agent can build standalone applications that use the Quantish MCP API. When building apps, ensure:
217
+
218
+ 1. **Use HTTP API** - Don't use MCP SDK directly
219
+ 2. **Environment Variables** - Store API keys in `.env`
220
+ 3. **Two Endpoints**:
221
+ - Discovery: `https://quantish.live/mcp/execute` (public)
222
+ - Trading: `https://quantish-sdk-production.up.railway.app/mcp/execute` (requires API key)
223
+
224
+ Example API call:
225
+
226
+ ```javascript
227
+ // Discovery MCP (simple format)
228
+ const response = await fetch('https://quantish.live/mcp/execute', {
229
+ method: 'POST',
230
+ headers: {
231
+ 'Content-Type': 'application/json',
232
+ 'X-API-Key': 'qm_ueQeqrmvZyHtR1zuVbLYkhx0fKyVAuV8'
233
+ },
234
+ body: JSON.stringify({
235
+ name: 'search_markets',
236
+ arguments: { query: 'bitcoin', limit: 5 }
237
+ })
238
+ });
239
+
240
+ // Trading MCP (JSON-RPC format)
241
+ const response = await fetch('https://quantish-sdk-production.up.railway.app/mcp/execute', {
242
+ method: 'POST',
243
+ headers: {
244
+ 'Content-Type': 'application/json',
245
+ 'X-API-Key': process.env.QUANTISH_API_KEY
246
+ },
247
+ body: JSON.stringify({
248
+ jsonrpc: '2.0',
249
+ id: 1,
250
+ method: 'tools/call',
251
+ params: {
252
+ name: 'get_positions',
253
+ arguments: {}
254
+ }
255
+ })
256
+ });
230
257
  ```
231
258
 
232
- The agent uses Claude to understand your requests and decide which tools to use. It can combine coding and trading tools in a single conversation.
259
+ ## Self-Hosting
260
+
261
+ You can self-host the Quantish MCP server:
262
+
263
+ ```bash
264
+ # Set custom server URL
265
+ quantish config --server https://your-server.com/mcp
266
+
267
+ # Or use environment variable
268
+ export MCP_SERVER_URL=https://your-server.com/mcp
269
+ ```
233
270
 
234
271
  ## Platform Support
235
272
 
@@ -239,24 +276,71 @@ The agent uses Claude to understand your requests and decide which tools to use.
239
276
  | Linux | ✅ Full support |
240
277
  | Windows | ⚠️ Requires WSL |
241
278
 
242
- **Windows users:** Install [WSL (Windows Subsystem for Linux)](https://learn.microsoft.com/en-us/windows/wsl/install) and run Quantish from within WSL. Native Windows (PowerShell/cmd.exe) is not supported.
279
+ ## Examples
280
+
281
+ ```bash
282
+ # Search for markets
283
+ quantish -p "find markets about bitcoin"
243
284
 
244
- ## Environment Variables
285
+ # Check positions
286
+ quantish -p "show my positions with P&L"
245
287
 
246
- | Variable | Description |
247
- |----------|-------------|
248
- | `ANTHROPIC_API_KEY` | Your Anthropic API key (required) |
249
- | `QUANTISH_API_KEY` | Your Quantish trading API key |
250
- | `EXA_API_KEY` | Optional: Exa AI search key for powerful web search |
288
+ # Build a trading bot
289
+ quantish
290
+ > Create a bot that monitors Trump markets and alerts me when prices change more than 5%
291
+
292
+ # Start a dev server
293
+ quantish
294
+ > Start my React app on port 3001
295
+
296
+ # Code review
297
+ quantish
298
+ > Review my trading bot code and suggest improvements
299
+ ```
300
+
301
+ ## Troubleshooting
302
+
303
+ ### Tool calls failing with malformed arguments
304
+
305
+ Some OpenRouter models (like GLM-4.7) occasionally emit malformed tool calls. The CLI includes robust parsing to handle these, but if issues persist:
306
+
307
+ ```bash
308
+ /model sonnet # Switch to Claude Sonnet
309
+ ```
310
+
311
+ ### Session not resuming
251
312
 
252
- ### Web Search
313
+ Sessions are stored in `~/.quantish/sessions/`. To reset:
253
314
 
254
- Web search works without API keys (using DuckDuckGo fallback), but **Exa is strongly recommended** for AI-quality search results.
315
+ ```bash
316
+ rm -rf ~/.quantish/sessions
317
+ ```
255
318
 
256
- Get your Exa API key at: https://dashboard.exa.ai
319
+ ### High token usage
257
320
 
258
- Exa is the same search engine used by Cursor, Notion, Vercel, and other leading AI products.
321
+ ```bash
322
+ /compact # Summarize conversation
323
+ /model haiku # Switch to cheaper model
324
+ /clear # Start fresh
325
+ ```
326
+
327
+ ## Development
328
+
329
+ ```bash
330
+ git clone https://github.com/joinQuantish/quantish-agent
331
+ cd quantish-agent
332
+ npm install
333
+ npm run build
334
+ npm link # Install locally
335
+ ```
259
336
 
260
337
  ## License
261
338
 
262
339
  MIT
340
+
341
+ ## Links
342
+
343
+ - [GitHub](https://github.com/joinQuantish/quantish-agent)
344
+ - [NPM](https://www.npmjs.com/package/@quantish/agent)
345
+ - [Documentation](https://docs.quantish.live)
346
+ - [Quantish Platform](https://quantish.live)
package/dist/index.js CHANGED
@@ -14,6 +14,8 @@ var DEFAULT_TRADING_MCP_URL = "https://quantish-sdk-production.up.railway.app/mc
14
14
  var DISCOVERY_MCP_URL = "https://quantish.live/mcp";
15
15
  var DISCOVERY_MCP_PUBLIC_KEY = "qm_ueQeqrmvZyHtR1zuVbLYkhx0fKyVAuV8";
16
16
  var DEFAULT_MCP_URL = DEFAULT_TRADING_MCP_URL;
17
+ var DEFAULT_ANTHROPIC_MODEL = "claude-sonnet-4-5-20250929";
18
+ var DEFAULT_OPENROUTER_MODEL = "z-ai/glm-4.7";
17
19
  var schema = {
18
20
  anthropicApiKey: {
19
21
  type: "string"
@@ -132,10 +134,13 @@ var ConfigManager = class {
132
134
  this.conf.set("mcpServerUrl", url);
133
135
  }
134
136
  /**
135
- * Get the model to use
137
+ * Get the model to use (returns default based on current provider)
136
138
  */
137
139
  getModel() {
138
- return this.conf.get("model") ?? "claude-sonnet-4-5-20250929";
140
+ const model = this.conf.get("model");
141
+ if (model) return model;
142
+ const provider = this.getProvider();
143
+ return provider === "openrouter" ? DEFAULT_OPENROUTER_MODEL : DEFAULT_ANTHROPIC_MODEL;
139
144
  }
140
145
  /**
141
146
  * Set the model to use
@@ -2208,114 +2213,6 @@ async function executeLocalTool(name, args) {
2208
2213
  return { success: false, error: `Unknown local tool: ${name}` };
2209
2214
  }
2210
2215
 
2211
- // src/agent/compaction.ts
2212
- var COMPACTION_PROMPT = `Your context window is filling up. Please create a concise summary of our conversation so far that will allow you to continue working effectively.
2213
-
2214
- The summary should be wrapped in <summary></summary> tags and include:
2215
-
2216
- # Task Overview
2217
- - The user's core request and goals
2218
- - Success criteria and constraints
2219
- - Any specific preferences mentioned
2220
-
2221
- # Current State
2222
- - What has been completed so far
2223
- - Files created or modified (with paths)
2224
- - Artifacts or outputs produced
2225
- - Current working directory if relevant
2226
-
2227
- # Important Discoveries
2228
- - Technical constraints or requirements found
2229
- - Key decisions made and why
2230
- - Errors encountered and how they were resolved
2231
- - Approaches that didn't work (to avoid repeating)
2232
-
2233
- # Next Steps
2234
- - Specific actions still needed
2235
- - Priority order if multiple steps remain
2236
- - Any blockers or dependencies
2237
-
2238
- # Context to Preserve
2239
- - User preferences or style requirements
2240
- - Domain-specific details that matter
2241
- - Any commitments or promises made
2242
-
2243
- Be thorough but concise. The goal is to capture everything needed to continue seamlessly, while reducing token usage significantly.`;
2244
- function parseCompactedSummary(response) {
2245
- const match = response.match(/<summary>([\s\S]*?)<\/summary>/);
2246
- if (match && match[1]) {
2247
- return match[1].trim();
2248
- }
2249
- return response.trim() || null;
2250
- }
2251
- async function createCompactedSummary(anthropic, history, model = "claude-sonnet-4-5-20250929", customPrompt) {
2252
- const prompt2 = customPrompt || COMPACTION_PROMPT;
2253
- const compactionMessages = [
2254
- ...history,
2255
- {
2256
- role: "user",
2257
- content: prompt2
2258
- }
2259
- ];
2260
- const response = await anthropic.messages.create({
2261
- model,
2262
- max_tokens: 4096,
2263
- messages: compactionMessages
2264
- });
2265
- const textBlocks = response.content.filter((block) => block.type === "text");
2266
- const fullText = textBlocks.map((block) => block.text).join("\n");
2267
- const summary = parseCompactedSummary(fullText);
2268
- if (!summary) {
2269
- throw new Error("Failed to parse compacted summary from response");
2270
- }
2271
- return summary;
2272
- }
2273
- function historyFromSummary(summary) {
2274
- return [
2275
- {
2276
- role: "assistant",
2277
- content: summary
2278
- }
2279
- ];
2280
- }
2281
- async function compactConversation(anthropic, history, model, systemPrompt, tools) {
2282
- let originalTokens = 0;
2283
- try {
2284
- const countResult = await anthropic.messages.countTokens({
2285
- model,
2286
- system: systemPrompt,
2287
- tools,
2288
- messages: history
2289
- });
2290
- originalTokens = countResult.input_tokens;
2291
- } catch (e) {
2292
- const contentLength = JSON.stringify(history).length;
2293
- originalTokens = Math.ceil(contentLength / 4);
2294
- }
2295
- const summaryModel = "claude-sonnet-4-5-20250929";
2296
- const summary = await createCompactedSummary(anthropic, history, summaryModel);
2297
- const newHistory = historyFromSummary(summary);
2298
- let newTokens = 0;
2299
- try {
2300
- const countResult = await anthropic.messages.countTokens({
2301
- model,
2302
- system: systemPrompt,
2303
- tools,
2304
- messages: newHistory
2305
- });
2306
- newTokens = countResult.input_tokens;
2307
- } catch (e) {
2308
- const contentLength = JSON.stringify(newHistory).length;
2309
- newTokens = Math.ceil(contentLength / 4);
2310
- }
2311
- return {
2312
- newHistory,
2313
- summary,
2314
- originalTokens,
2315
- newTokens
2316
- };
2317
- }
2318
-
2319
2216
  // src/agent/pricing.ts
2320
2217
  var MODELS = {
2321
2218
  "claude-opus-4-5-20250929": {
@@ -2937,17 +2834,33 @@ var OpenRouterClient = class {
2937
2834
  }
2938
2835
  };
2939
2836
  function calculateOpenRouterCost(modelId, inputTokens, outputTokens, cacheReadTokens = 0, cacheWriteTokens = 0) {
2940
- const config = getOpenRouterModelConfig(modelId);
2837
+ let config = getOpenRouterModelConfig(modelId);
2838
+ if (!config) {
2839
+ config = getOpenRouterModelConfig(modelId.toLowerCase());
2840
+ }
2841
+ if (!config) {
2842
+ const lower = modelId.toLowerCase();
2843
+ for (const [key, model] of Object.entries(OPENROUTER_MODELS2)) {
2844
+ if (key.toLowerCase() === lower || model.name.toLowerCase() === lower) {
2845
+ config = model;
2846
+ break;
2847
+ }
2848
+ }
2849
+ if (!config && OPENROUTER_ALIASES[lower]) {
2850
+ config = OPENROUTER_MODELS2[OPENROUTER_ALIASES[lower]];
2851
+ }
2852
+ }
2941
2853
  const pricing = config?.pricing ?? {
2942
- inputPerMTok: 1,
2943
- outputPerMTok: 3,
2944
- cacheReadPerMTok: 0.1,
2945
- cacheWritePerMTok: 1.25
2854
+ inputPerMTok: 0.4,
2855
+ // GLM 4.7 pricing as fallback
2856
+ outputPerMTok: 1.5,
2857
+ cacheReadPerMTok: 0,
2858
+ cacheWritePerMTok: 0
2946
2859
  };
2947
2860
  const inputCost = inputTokens / 1e6 * pricing.inputPerMTok;
2948
2861
  const outputCost = outputTokens / 1e6 * pricing.outputPerMTok;
2949
- const cacheReadCost = cacheReadTokens / 1e6 * (pricing.cacheReadPerMTok ?? pricing.inputPerMTok * 0.1);
2950
- const cacheWriteCost = cacheWriteTokens / 1e6 * (pricing.cacheWritePerMTok ?? pricing.inputPerMTok * 1.25);
2862
+ const cacheReadCost = cacheReadTokens / 1e6 * (pricing.cacheReadPerMTok ?? 0);
2863
+ const cacheWriteCost = cacheWriteTokens / 1e6 * (pricing.cacheWritePerMTok ?? 0);
2951
2864
  return {
2952
2865
  inputCost,
2953
2866
  outputCost,
@@ -3264,7 +3177,34 @@ var OpenRouterProvider = class {
3264
3177
  if (!tc || !tc.name) {
3265
3178
  continue;
3266
3179
  }
3180
+ let toolName = tc.name;
3181
+ if (toolName.includes("<")) {
3182
+ toolName = toolName.split("<")[0];
3183
+ }
3184
+ if (toolName.includes("(")) {
3185
+ toolName = toolName.split("(")[0];
3186
+ }
3187
+ toolName = toolName.trim();
3267
3188
  let args = tc.arguments?.trim() || "{}";
3189
+ if (args.includes("<arg_key>") || args.includes("</arg_key>")) {
3190
+ args = args.replace(/<\/?arg_key>/g, "");
3191
+ if (!args.startsWith("{")) {
3192
+ const keyValuePairs = [];
3193
+ const kvMatches = args.matchAll(/(\w+):\s*(?:"([^"]+)"|(\d+)|(\w+))/g);
3194
+ for (const match of kvMatches) {
3195
+ const key = match[1];
3196
+ const value = match[2] ?? match[3] ?? match[4];
3197
+ if (match[3]) {
3198
+ keyValuePairs.push(`"${key}": ${value}`);
3199
+ } else {
3200
+ keyValuePairs.push(`"${key}": "${value}"`);
3201
+ }
3202
+ }
3203
+ if (keyValuePairs.length > 0) {
3204
+ args = `{${keyValuePairs.join(", ")}}`;
3205
+ }
3206
+ }
3207
+ }
3268
3208
  if (args && !args.endsWith("}") && !args.endsWith("]")) {
3269
3209
  const openBraces = (args.match(/{/g) || []).length;
3270
3210
  const closeBraces = (args.match(/}/g) || []).length;
@@ -3277,10 +3217,10 @@ var OpenRouterProvider = class {
3277
3217
  }
3278
3218
  const input = JSON.parse(args);
3279
3219
  const toolId = tc.id || `tool_${Date.now()}_${Math.random().toString(36).slice(2)}`;
3280
- toolCalls.push({ id: toolId, name: tc.name, input });
3281
- callbacks.onToolCall?.(toolId, tc.name, input);
3220
+ toolCalls.push({ id: toolId, name: toolName, input });
3221
+ callbacks.onToolCall?.(toolId, toolName, input);
3282
3222
  } catch (e) {
3283
- const toolName = tc?.name || "unknown_tool";
3223
+ const cleanToolName = tc?.name?.split("<")[0]?.split("(")[0]?.trim() || "unknown_tool";
3284
3224
  let parsedInput = {};
3285
3225
  try {
3286
3226
  const argsStr = tc?.arguments || "";
@@ -3291,8 +3231,8 @@ var OpenRouterProvider = class {
3291
3231
  } catch {
3292
3232
  }
3293
3233
  const toolId = tc?.id || `tool_${Date.now()}_${Math.random().toString(36).slice(2)}`;
3294
- toolCalls.push({ id: toolId, name: toolName, input: parsedInput });
3295
- callbacks.onToolCall?.(toolId, toolName, parsedInput);
3234
+ toolCalls.push({ id: toolId, name: cleanToolName, input: parsedInput });
3235
+ callbacks.onToolCall?.(toolId, cleanToolName, parsedInput);
3296
3236
  }
3297
3237
  }
3298
3238
  const cost = calculateOpenRouterCost(
@@ -3689,7 +3629,7 @@ ${userMessage}`;
3689
3629
  output_tokens: response.usage.outputTokens,
3690
3630
  cache_creation_input_tokens: response.usage.cacheCreationTokens,
3691
3631
  cache_read_input_tokens: response.usage.cacheReadTokens
3692
- });
3632
+ }, response.cost);
3693
3633
  const responseContent = [];
3694
3634
  if (response.text) {
3695
3635
  responseContent.push({ type: "text", text: response.text });
@@ -4026,15 +3966,17 @@ ${userMessage}`;
4026
3966
  }
4027
3967
  /**
4028
3968
  * Update cumulative token usage from API response
3969
+ * @param usage - Token counts from the API response
3970
+ * @param preCalculatedCost - Optional pre-calculated cost (from OpenRouter provider)
4029
3971
  */
4030
- updateTokenUsage(usage) {
3972
+ updateTokenUsage(usage, preCalculatedCost) {
4031
3973
  const model = this.config.model ?? DEFAULT_MODEL;
4032
3974
  this.cumulativeTokenUsage.inputTokens = usage.input_tokens;
4033
3975
  this.cumulativeTokenUsage.outputTokens += usage.output_tokens;
4034
3976
  this.cumulativeTokenUsage.cacheCreationInputTokens = usage.cache_creation_input_tokens || 0;
4035
3977
  this.cumulativeTokenUsage.cacheReadInputTokens = usage.cache_read_input_tokens || 0;
4036
3978
  this.cumulativeTokenUsage.totalTokens = this.cumulativeTokenUsage.inputTokens + this.cumulativeTokenUsage.outputTokens;
4037
- const callCost = calculateCost(
3979
+ const callCost = preCalculatedCost ?? calculateCost(
4038
3980
  model,
4039
3981
  usage.input_tokens,
4040
3982
  usage.output_tokens,
@@ -4135,16 +4077,13 @@ ${userMessage}`;
4135
4077
  /**
4136
4078
  * Compact the conversation history to reduce token usage.
4137
4079
  *
4138
- * This uses Claude to create a structured summary of the conversation,
4080
+ * This uses the current LLM to create a structured summary of the conversation,
4139
4081
  * then replaces the history with just the summary. This dramatically
4140
4082
  * reduces token count while preserving important context.
4141
4083
  *
4142
4084
  * @returns Object with original/new token counts and the summary
4143
4085
  */
4144
4086
  async compactHistory() {
4145
- const model = this.config.model ?? "claude-sonnet-4-5-20250929";
4146
- const systemPrompt = this.config.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
4147
- const allTools = await this.getAllTools();
4148
4087
  if (this.conversationHistory.length < 2) {
4149
4088
  return {
4150
4089
  success: false,
@@ -4154,23 +4093,54 @@ ${userMessage}`;
4154
4093
  };
4155
4094
  }
4156
4095
  try {
4157
- const result = await compactConversation(
4158
- this.anthropic,
4159
- this.conversationHistory,
4160
- model,
4161
- systemPrompt,
4162
- allTools
4163
- );
4164
- this.conversationHistory = result.newHistory;
4096
+ const originalContentLength = JSON.stringify(this.conversationHistory).length;
4097
+ const originalTokens = Math.ceil(originalContentLength / 4);
4098
+ const compactionPrompt = `Your context window is filling up. Create a concise summary of our conversation so far.
4099
+
4100
+ Include:
4101
+ - User's main goals and what was accomplished
4102
+ - Files created/modified (with paths)
4103
+ - Key decisions and discoveries
4104
+ - Next steps still needed
4105
+ - Any important context to preserve
4106
+
4107
+ Be thorough but concise. The goal is to capture everything needed to continue seamlessly.`;
4108
+ const compactionMessages = [
4109
+ ...this.conversationHistory,
4110
+ { role: "user", content: compactionPrompt }
4111
+ ];
4112
+ let summary;
4113
+ if (this.config.provider === "openrouter" && this.llmProvider) {
4114
+ const response = await this.llmProvider.chat(compactionMessages);
4115
+ summary = response.text;
4116
+ } else {
4117
+ const model = this.config.model ?? DEFAULT_MODEL;
4118
+ const response = await this.anthropic.messages.create({
4119
+ model,
4120
+ max_tokens: 4096,
4121
+ messages: compactionMessages
4122
+ });
4123
+ const textBlocks = response.content.filter((block) => block.type === "text");
4124
+ summary = textBlocks.map((block) => block.text).join("\n");
4125
+ }
4126
+ if (!summary || summary.trim().length === 0) {
4127
+ throw new Error("Failed to generate summary");
4128
+ }
4129
+ const newHistory = [
4130
+ { role: "assistant", content: summary.trim() }
4131
+ ];
4132
+ const newContentLength = JSON.stringify(newHistory).length;
4133
+ const newTokens = Math.ceil(newContentLength / 4);
4134
+ this.conversationHistory = newHistory;
4165
4135
  this.resetTokenUsage();
4166
- this.cumulativeTokenUsage.inputTokens = result.newTokens;
4167
- this.cumulativeTokenUsage.totalTokens = result.newTokens;
4136
+ this.cumulativeTokenUsage.inputTokens = newTokens;
4137
+ this.cumulativeTokenUsage.totalTokens = newTokens;
4168
4138
  this.config.onTokenUsage?.(this.cumulativeTokenUsage);
4169
4139
  return {
4170
4140
  success: true,
4171
- summary: result.summary,
4172
- originalTokenCount: result.originalTokens,
4173
- newTokenCount: result.newTokens
4141
+ summary: summary.trim(),
4142
+ originalTokenCount: originalTokens,
4143
+ newTokenCount: newTokens
4174
4144
  };
4175
4145
  } catch (error2) {
4176
4146
  return {
@@ -5095,16 +5065,22 @@ Use /load <id> to load a session.`
5095
5065
  setIsProcessing(false);
5096
5066
  setThinkingText(null);
5097
5067
  abortController.current = null;
5098
- if (hasQueuedMessage && queuedInput) {
5099
- const nextMessage = queuedInput;
5100
- setQueuedInput("");
5101
- setHasQueuedMessage(false);
5102
- setTimeout(() => {
5103
- handleSubmit(nextMessage);
5104
- }, 100);
5105
- }
5106
5068
  }
5107
- }, [agent, isProcessing, isInterrupted, exit, onExit, handleSlashCommand, hasQueuedMessage, queuedInput]);
5069
+ }, [agent, isProcessing, isInterrupted, exit, onExit, handleSlashCommand]);
5070
+ useEffect(() => {
5071
+ if (!isProcessing && hasQueuedMessage && queuedInput) {
5072
+ const nextMessage = queuedInput;
5073
+ setQueuedInput("");
5074
+ setHasQueuedMessage(false);
5075
+ setMessages((prev) => prev.filter(
5076
+ (m) => !(m.role === "system" && m.content.startsWith("\u{1F4E5} Queued:"))
5077
+ ));
5078
+ const timer = setTimeout(() => {
5079
+ handleSubmit(nextMessage);
5080
+ }, 150);
5081
+ return () => clearTimeout(timer);
5082
+ }
5083
+ }, [isProcessing, hasQueuedMessage, queuedInput, handleSubmit]);
5108
5084
  useEffect(() => {
5109
5085
  const originalConfig = agent.config;
5110
5086
  agent.config = {
@@ -5159,6 +5135,14 @@ Stopped ${count} background process${count > 1 ? "es" : ""}.`);
5159
5135
  onExit?.();
5160
5136
  exit();
5161
5137
  }
5138
+ if (key.backspace && input === "" && hasQueuedMessage && queuedInput) {
5139
+ setInput(queuedInput);
5140
+ setQueuedInput("");
5141
+ setHasQueuedMessage(false);
5142
+ setMessages((prev) => prev.filter(
5143
+ (m) => !(m.role === "system" && m.content.startsWith("\u{1F4E5} Queued:"))
5144
+ ));
5145
+ }
5162
5146
  if (key.escape && isProcessing) {
5163
5147
  setIsInterrupted(true);
5164
5148
  abortController.current?.abort();
@@ -5235,9 +5219,10 @@ Stopped ${count} background process${count > 1 ? "es" : ""}.`);
5235
5219
  "\u274C Error: ",
5236
5220
  error2
5237
5221
  ] }) }),
5238
- isProcessing && !streamingText && currentToolCalls.length === 0 && /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
5222
+ isProcessing && /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
5239
5223
  /* @__PURE__ */ jsx(Spinner, { type: "dots" }),
5240
- " Thinking..."
5224
+ " ",
5225
+ currentToolCalls.length > 0 ? `Working... (${currentToolCalls.filter((tc) => tc.pending).length} tool${currentToolCalls.filter((tc) => tc.pending).length !== 1 ? "s" : ""} running)` : streamingText ? "Generating..." : "Thinking..."
5241
5226
  ] }) }),
5242
5227
  input.startsWith("/") && !isProcessing && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, paddingLeft: 2, children: [
5243
5228
  /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: "Commands:" }),
@@ -5299,7 +5284,7 @@ Stopped ${count} background process${count > 1 ? "es" : ""}.`);
5299
5284
  }
5300
5285
 
5301
5286
  // src/index.ts
5302
- var VERSION = "0.1.22";
5287
+ var VERSION = "0.1.24";
5303
5288
  function cleanup() {
5304
5289
  if (processManager.hasRunning()) {
5305
5290
  const count = processManager.runningCount();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantish/agent",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "description": "AI-powered agent for building trading bots on Polymarket",
5
5
  "type": "module",
6
6
  "bin": {