@quantish/agent 0.1.43 → 0.1.45
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/dist/index.js +158 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -674,9 +674,40 @@ async function runSetup() {
|
|
|
674
674
|
let skipTrading = false;
|
|
675
675
|
if (quantishKey) {
|
|
676
676
|
console.log(chalk.dim(`Current trading key: ${quantishKey.slice(0, 12)}...`));
|
|
677
|
-
const action = await prompt("Keep
|
|
677
|
+
const action = await prompt("Keep (Enter), new key (n), create wallet (c), or disable (d): ");
|
|
678
678
|
if (action.toLowerCase() === "n") {
|
|
679
679
|
quantishKey = await prompt("Enter your Quantish Trading API key: ", true);
|
|
680
|
+
} else if (action.toLowerCase() === "c") {
|
|
681
|
+
console.log(chalk.dim("\nCreating a new wallet on Quantish Signing Server..."));
|
|
682
|
+
const externalId = await prompt("Enter a unique identifier (e.g., email or username): ");
|
|
683
|
+
if (!externalId) {
|
|
684
|
+
console.log(chalk.red("Identifier is required to create an account."));
|
|
685
|
+
console.log(chalk.dim("Keeping current key.\n"));
|
|
686
|
+
} else {
|
|
687
|
+
try {
|
|
688
|
+
const mcpClient = createMCPClient(config.getTradingMcpUrl(), "");
|
|
689
|
+
const result = await mcpClient.callTool("request_api_key", { externalId });
|
|
690
|
+
if (result.success && typeof result.data === "object" && result.data !== null) {
|
|
691
|
+
const data = result.data;
|
|
692
|
+
quantishKey = data.apiKey;
|
|
693
|
+
console.log(chalk.green("\n\u2713 New wallet created!"));
|
|
694
|
+
console.log(chalk.dim(` EOA Address: ${data.eoaAddress}`));
|
|
695
|
+
console.log(chalk.dim(" (Your Safe wallet will deploy on first trade)\n"));
|
|
696
|
+
if (data.apiSecret) {
|
|
697
|
+
console.log(chalk.yellow("\u26A0\uFE0F Save your API secret (shown only once):"));
|
|
698
|
+
console.log(chalk.bold.yellow(` ${String(data.apiSecret)}`));
|
|
699
|
+
console.log();
|
|
700
|
+
}
|
|
701
|
+
} else {
|
|
702
|
+
console.log(chalk.red("Failed to create wallet: " + (result.error || "Unknown error")));
|
|
703
|
+
console.log(chalk.dim("Keeping current key.\n"));
|
|
704
|
+
}
|
|
705
|
+
} catch (error2) {
|
|
706
|
+
console.log(chalk.red("Failed to connect to Quantish Trading Server."));
|
|
707
|
+
console.log(chalk.dim(String(error2)));
|
|
708
|
+
console.log(chalk.dim("Keeping current key.\n"));
|
|
709
|
+
}
|
|
710
|
+
}
|
|
680
711
|
} else if (action.toLowerCase() === "d") {
|
|
681
712
|
quantishKey = void 0;
|
|
682
713
|
skipTrading = true;
|
|
@@ -740,9 +771,39 @@ async function runSetup() {
|
|
|
740
771
|
let skipKalshi = false;
|
|
741
772
|
if (kalshiKey) {
|
|
742
773
|
console.log(chalk.dim(`Current Kalshi key: ${kalshiKey.slice(0, 12)}...`));
|
|
743
|
-
const action = await prompt("Keep
|
|
774
|
+
const action = await prompt("Keep (Enter), new key (n), create wallet (c), or disable (d): ");
|
|
744
775
|
if (action.toLowerCase() === "n") {
|
|
745
776
|
kalshiKey = await prompt("Enter your Kalshi API key: ", true);
|
|
777
|
+
} else if (action.toLowerCase() === "c") {
|
|
778
|
+
console.log(chalk.dim("\nCreating a new Solana wallet on Kalshi MCP..."));
|
|
779
|
+
const externalId = await prompt("Enter a unique identifier (e.g., email or username): ");
|
|
780
|
+
if (!externalId) {
|
|
781
|
+
console.log(chalk.red("Identifier is required to create an account."));
|
|
782
|
+
console.log(chalk.dim("Keeping current key.\n"));
|
|
783
|
+
} else {
|
|
784
|
+
try {
|
|
785
|
+
const kalshiClient = createMCPClient(KALSHI_MCP_URL, "", "kalshi");
|
|
786
|
+
const result = await kalshiClient.callTool("kalshi_signup", { externalId });
|
|
787
|
+
if (result.success && typeof result.data === "object" && result.data !== null) {
|
|
788
|
+
const data = result.data;
|
|
789
|
+
kalshiKey = data.apiKey;
|
|
790
|
+
console.log(chalk.green("\n\u2713 New Kalshi wallet created!"));
|
|
791
|
+
console.log(chalk.dim(` Solana Address: ${data.walletAddress}`));
|
|
792
|
+
if (data.apiSecret) {
|
|
793
|
+
console.log(chalk.yellow("\n\u26A0\uFE0F Save your API secret (shown only once):"));
|
|
794
|
+
console.log(chalk.bold.yellow(` ${String(data.apiSecret)}`));
|
|
795
|
+
console.log();
|
|
796
|
+
}
|
|
797
|
+
} else {
|
|
798
|
+
console.log(chalk.red("Failed to create wallet: " + (result.error || "Unknown error")));
|
|
799
|
+
console.log(chalk.dim("Keeping current key.\n"));
|
|
800
|
+
}
|
|
801
|
+
} catch (error2) {
|
|
802
|
+
console.log(chalk.red("Failed to connect to Kalshi MCP."));
|
|
803
|
+
console.log(chalk.dim(String(error2)));
|
|
804
|
+
console.log(chalk.dim("Keeping current key.\n"));
|
|
805
|
+
}
|
|
806
|
+
}
|
|
746
807
|
} else if (action.toLowerCase() === "d") {
|
|
747
808
|
kalshiKey = void 0;
|
|
748
809
|
skipKalshi = true;
|
|
@@ -3588,21 +3649,27 @@ var DEFAULT_SYSTEM_PROMPT = `You are Quantish, an AI trading agent for predictio
|
|
|
3588
3649
|
|
|
3589
3650
|
When user asks to find markets:
|
|
3590
3651
|
1. Call search_markets ONCE with a good query
|
|
3591
|
-
2. Present results in a
|
|
3652
|
+
2. Present results in a table that INCLUDES PRICES from the response:
|
|
3653
|
+
| Market | Yes Price | No Price | Volume | End Date |
|
|
3654
|
+
The response has: markets[].markets[].outcomes[].price (0.05 = 5% probability)
|
|
3592
3655
|
3. STOP. Wait for user to ask for more.
|
|
3593
3656
|
|
|
3594
3657
|
**DO NOT** make multiple searches or call get_market_details on every result.
|
|
3595
|
-
search_markets already returns prices, volume, and
|
|
3658
|
+
search_markets already returns prices, volume, resolution criteria, and outcome probabilities.
|
|
3596
3659
|
|
|
3597
3660
|
## Tools Available
|
|
3598
3661
|
|
|
3599
|
-
**Discovery MCP** (market data):
|
|
3600
|
-
- search_markets(query, limit=10) \u2192 Markets
|
|
3601
|
-
- get_market_details(platform, marketId) \u2192
|
|
3662
|
+
**Discovery MCP** (market data - prices included):
|
|
3663
|
+
- search_markets(query, limit=10) \u2192 Markets WITH prices from Polymarket/Kalshi/Limitless
|
|
3664
|
+
- get_market_details(platform, marketId) \u2192 Full details WITH prices for ONE market
|
|
3602
3665
|
- get_trending_markets(limit=10) \u2192 Hot markets by volume
|
|
3603
3666
|
|
|
3604
|
-
**Polymarket Trading
|
|
3605
|
-
- place_order, cancel_order, get_orders, get_positions, get_balances
|
|
3667
|
+
**Polymarket Trading** (requires conditionId from Discovery results):
|
|
3668
|
+
- place_order, cancel_order, get_orders, get_positions, get_balances
|
|
3669
|
+
- get_price(tokenId) \u2192 Live price (only if you need real-time, Discovery already has prices)
|
|
3670
|
+
- get_orderbook(tokenId) \u2192 Bid/ask depth
|
|
3671
|
+
|
|
3672
|
+
NOTE: Don't use get_market - use get_market_details from Discovery instead.
|
|
3606
3673
|
|
|
3607
3674
|
**Kalshi Trading** (via DFlow):
|
|
3608
3675
|
- kalshi_buy_yes, kalshi_buy_no, kalshi_get_positions, kalshi_get_balances
|
|
@@ -3614,10 +3681,22 @@ search_markets already returns prices, volume, and liquidity.
|
|
|
3614
3681
|
- get_process_output, list_processes, stop_process
|
|
3615
3682
|
- git operations: status, diff, add, commit
|
|
3616
3683
|
|
|
3684
|
+
## CRITICAL: File Operations
|
|
3685
|
+
|
|
3686
|
+
**NEVER repeat the same operation.** If write_file or edit_file fails:
|
|
3687
|
+
1. Stop and tell the user what went wrong
|
|
3688
|
+
2. Do NOT retry with the same content
|
|
3689
|
+
3. Do NOT delete and rewrite - use edit_file to fix specific issues
|
|
3690
|
+
|
|
3691
|
+
When writing code files:
|
|
3692
|
+
- Write complete, valid code (not JSON-escaped strings)
|
|
3693
|
+
- Create one file at a time, verify it works
|
|
3694
|
+
- If you get stuck, ask the user for help
|
|
3695
|
+
|
|
3617
3696
|
## Building Trading Bots
|
|
3618
3697
|
|
|
3619
3698
|
When user wants to build an app or bot:
|
|
3620
|
-
1.
|
|
3699
|
+
1. Create files one at a time with write_file
|
|
3621
3700
|
2. The MCP servers are HTTP APIs - apps can call them directly
|
|
3622
3701
|
3. Use start_background_process for dev servers
|
|
3623
3702
|
4. API endpoints:
|
|
@@ -3629,7 +3708,7 @@ When user wants to build an app or bot:
|
|
|
3629
3708
|
- Kalshi: percentages like 5% YES
|
|
3630
3709
|
|
|
3631
3710
|
Be concise. Present results clearly. Wait for user input.`;
|
|
3632
|
-
var Agent = class {
|
|
3711
|
+
var Agent = class _Agent {
|
|
3633
3712
|
anthropic;
|
|
3634
3713
|
llmProvider;
|
|
3635
3714
|
mcpClient;
|
|
@@ -3639,6 +3718,11 @@ var Agent = class {
|
|
|
3639
3718
|
workingDirectory;
|
|
3640
3719
|
sessionCost = 0;
|
|
3641
3720
|
// Cumulative cost for this session
|
|
3721
|
+
// Loop detection: track last N tool calls to detect loops
|
|
3722
|
+
recentToolCalls = [];
|
|
3723
|
+
static MAX_RECENT_TOOL_CALLS = 5;
|
|
3724
|
+
static LOOP_THRESHOLD = 2;
|
|
3725
|
+
// Abort if same call appears this many times
|
|
3642
3726
|
cumulativeTokenUsage = {
|
|
3643
3727
|
inputTokens: 0,
|
|
3644
3728
|
outputTokens: 0,
|
|
@@ -3742,10 +3826,16 @@ ${userMessage}`;
|
|
|
3742
3826
|
role: "user",
|
|
3743
3827
|
content: contextMessage
|
|
3744
3828
|
});
|
|
3829
|
+
this.clearToolCallLoopTracking();
|
|
3745
3830
|
const toolCalls = [];
|
|
3746
3831
|
let iterations = 0;
|
|
3747
3832
|
let finalText = "";
|
|
3748
|
-
|
|
3833
|
+
const maxTurns = this.config.maxTurns ?? maxIterations;
|
|
3834
|
+
while (iterations < maxTurns) {
|
|
3835
|
+
if (this.config.abortSignal?.aborted) {
|
|
3836
|
+
finalText += "\n\n[Operation cancelled by user]";
|
|
3837
|
+
break;
|
|
3838
|
+
}
|
|
3749
3839
|
iterations++;
|
|
3750
3840
|
this.config.onStreamStart?.();
|
|
3751
3841
|
let response;
|
|
@@ -3862,6 +3952,18 @@ ${userMessage}`;
|
|
|
3862
3952
|
* Execute a tool (local or MCP)
|
|
3863
3953
|
*/
|
|
3864
3954
|
async executeTool(name, args) {
|
|
3955
|
+
if (this.config.abortSignal?.aborted) {
|
|
3956
|
+
return {
|
|
3957
|
+
result: { error: "Operation cancelled by user" },
|
|
3958
|
+
source: "local"
|
|
3959
|
+
};
|
|
3960
|
+
}
|
|
3961
|
+
if (this.checkToolCallLoop(name, args)) {
|
|
3962
|
+
return {
|
|
3963
|
+
result: { error: `Loop detected: "${name}" was called multiple times with the same input. Please try a different approach.` },
|
|
3964
|
+
source: "local"
|
|
3965
|
+
};
|
|
3966
|
+
}
|
|
3865
3967
|
if (isLocalTool(name)) {
|
|
3866
3968
|
const result = await executeLocalTool(name, args);
|
|
3867
3969
|
return {
|
|
@@ -3889,10 +3991,19 @@ ${userMessage}`;
|
|
|
3889
3991
|
source: "local"
|
|
3890
3992
|
};
|
|
3891
3993
|
}
|
|
3994
|
+
/**
|
|
3995
|
+
* Set the abort signal for the current request (call before run())
|
|
3996
|
+
*/
|
|
3997
|
+
setAbortSignal(signal) {
|
|
3998
|
+
this.config.abortSignal = signal;
|
|
3999
|
+
}
|
|
3892
4000
|
/**
|
|
3893
4001
|
* Run the agent with a user message (supports streaming)
|
|
3894
4002
|
*/
|
|
3895
|
-
async run(userMessage) {
|
|
4003
|
+
async run(userMessage, options) {
|
|
4004
|
+
if (options?.abortSignal) {
|
|
4005
|
+
this.config.abortSignal = options.abortSignal;
|
|
4006
|
+
}
|
|
3896
4007
|
if (this.config.provider === "openrouter") {
|
|
3897
4008
|
return this.runWithProvider(userMessage);
|
|
3898
4009
|
}
|
|
@@ -3910,10 +4021,16 @@ ${userMessage}`;
|
|
|
3910
4021
|
role: "user",
|
|
3911
4022
|
content: contextMessage
|
|
3912
4023
|
});
|
|
4024
|
+
this.clearToolCallLoopTracking();
|
|
3913
4025
|
const toolCalls = [];
|
|
3914
4026
|
let iterations = 0;
|
|
3915
4027
|
let finalText = "";
|
|
3916
|
-
|
|
4028
|
+
const maxTurns = this.config.maxTurns ?? maxIterations;
|
|
4029
|
+
while (iterations < maxTurns) {
|
|
4030
|
+
if (this.config.abortSignal?.aborted) {
|
|
4031
|
+
finalText += "\n\n[Operation cancelled by user]";
|
|
4032
|
+
break;
|
|
4033
|
+
}
|
|
3917
4034
|
iterations++;
|
|
3918
4035
|
this.config.onStreamStart?.();
|
|
3919
4036
|
let response;
|
|
@@ -4118,6 +4235,32 @@ ${userMessage}`;
|
|
|
4118
4235
|
* @param usage - Token counts from the API response
|
|
4119
4236
|
* @param preCalculatedCost - Optional pre-calculated cost (from OpenRouter provider)
|
|
4120
4237
|
*/
|
|
4238
|
+
/**
|
|
4239
|
+
* Check if a tool call would create a loop (same call repeated too many times).
|
|
4240
|
+
* Returns true if this call is part of a loop and should be stopped.
|
|
4241
|
+
*/
|
|
4242
|
+
checkToolCallLoop(toolName, input) {
|
|
4243
|
+
const inputStr = JSON.stringify(input);
|
|
4244
|
+
const callSignature = `${toolName}:${inputStr}`;
|
|
4245
|
+
this.recentToolCalls.push({ name: toolName, input: inputStr });
|
|
4246
|
+
if (this.recentToolCalls.length > _Agent.MAX_RECENT_TOOL_CALLS) {
|
|
4247
|
+
this.recentToolCalls.shift();
|
|
4248
|
+
}
|
|
4249
|
+
const duplicateCount = this.recentToolCalls.filter(
|
|
4250
|
+
(call) => call.name === toolName && call.input === inputStr
|
|
4251
|
+
).length;
|
|
4252
|
+
if (duplicateCount >= _Agent.LOOP_THRESHOLD) {
|
|
4253
|
+
console.warn(`[Loop Detection] Tool "${toolName}" called ${duplicateCount} times with identical input. Stopping loop.`);
|
|
4254
|
+
return true;
|
|
4255
|
+
}
|
|
4256
|
+
return false;
|
|
4257
|
+
}
|
|
4258
|
+
/**
|
|
4259
|
+
* Clear the tool call loop tracking (call when starting a new user message)
|
|
4260
|
+
*/
|
|
4261
|
+
clearToolCallLoopTracking() {
|
|
4262
|
+
this.recentToolCalls = [];
|
|
4263
|
+
}
|
|
4121
4264
|
updateTokenUsage(usage, preCalculatedCost) {
|
|
4122
4265
|
const model = this.config.model ?? DEFAULT_MODEL;
|
|
4123
4266
|
this.cumulativeTokenUsage.inputTokens = usage.input_tokens;
|
|
@@ -5161,7 +5304,7 @@ Use /load <id> to load a session.`
|
|
|
5161
5304
|
completedToolCalls.current = [];
|
|
5162
5305
|
abortController.current = new AbortController();
|
|
5163
5306
|
try {
|
|
5164
|
-
const result = await agent.run(trimmed);
|
|
5307
|
+
const result = await agent.run(trimmed, { abortSignal: abortController.current.signal });
|
|
5165
5308
|
if (isInterrupted) {
|
|
5166
5309
|
setMessages((prev) => [...prev, {
|
|
5167
5310
|
role: "system",
|