@quantish/agent 0.1.51 → 0.1.52

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/LICENSE +2 -0
  2. package/dist/index.js +164 -50
  3. package/package.json +1 -1
package/LICENSE CHANGED
@@ -142,3 +142,5 @@ please contact Quantish Inc. for a commercial license:
142
142
 
143
143
  Email: hello@quantish.live
144
144
  Website: https://quantish.live
145
+
146
+
package/dist/index.js CHANGED
@@ -3581,7 +3581,20 @@ function createLLMProvider(config) {
3581
3581
  // src/agent/loop.ts
3582
3582
  var DEFAULT_SYSTEM_PROMPT = `You are Quantish, an AI trading agent for prediction markets (Polymarket, Kalshi).
3583
3583
 
3584
- You have tools to search markets and place trades. When showing market data, display ALL relevant information from the response including prices/probabilities.
3584
+ ## CRITICAL: Market Display Rules
3585
+
3586
+ When showing market search results, ALWAYS include:
3587
+ - Market title
3588
+ - Platform
3589
+ - **Price/Probability** (REQUIRED - never omit this)
3590
+ - Market ID
3591
+
3592
+ Format market tables like this:
3593
+ | Market | Platform | Price | ID |
3594
+ |--------|----------|-------|-----|
3595
+ | Example market | Polymarket | Yes 45\xA2 / No 55\xA2 | 12345 |
3596
+
3597
+ The price data is in the tool result - extract and display it.
3585
3598
 
3586
3599
  ## Building Applications
3587
3600
 
@@ -3622,6 +3635,12 @@ var Agent = class _Agent {
3622
3635
  cost: { inputCost: 0, outputCost: 0, cacheWriteCost: 0, cacheReadCost: 0, totalCost: 0 },
3623
3636
  sessionCost: 0
3624
3637
  };
3638
+ // Sliding window context management
3639
+ conversationSummary = null;
3640
+ toolHistory = [];
3641
+ exchanges = [];
3642
+ static MAX_TOOL_HISTORY = 10;
3643
+ static MAX_EXCHANGES = 5;
3625
3644
  constructor(config) {
3626
3645
  this.config = {
3627
3646
  enableLocalTools: true,
@@ -3683,11 +3702,8 @@ var Agent = class _Agent {
3683
3702
  * Get or create the LLM provider instance
3684
3703
  */
3685
3704
  async getOrCreateProvider() {
3686
- if (this.llmProvider) {
3687
- return this.llmProvider;
3688
- }
3689
3705
  const allTools = await this.getAllTools();
3690
- const systemPrompt = this.config.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
3706
+ const systemPrompt = this.buildSystemContext();
3691
3707
  const defaultModel = this.config.provider === "openrouter" ? "z-ai/glm-4.7" : DEFAULT_MODEL;
3692
3708
  const model = this.config.model ?? defaultModel;
3693
3709
  const maxTokens = this.config.maxTokens ?? 8192;
@@ -3709,14 +3725,9 @@ var Agent = class _Agent {
3709
3725
  const maxIterations = this.config.maxIterations ?? 200;
3710
3726
  const useStreaming = this.config.streaming ?? true;
3711
3727
  const provider = await this.getOrCreateProvider();
3712
- const contextMessage = `[Working directory: ${this.workingDirectory}]
3713
-
3714
- ${userMessage}`;
3715
- this.conversationHistory.push({
3716
- role: "user",
3717
- content: contextMessage
3718
- });
3728
+ const messages = this.buildSlimHistory(userMessage);
3719
3729
  this.clearToolCallLoopTracking();
3730
+ let currentTurnMessages = [...messages];
3720
3731
  const toolCalls = [];
3721
3732
  let iterations = 0;
3722
3733
  let finalText = "";
@@ -3730,7 +3741,7 @@ ${userMessage}`;
3730
3741
  this.config.onStreamStart?.();
3731
3742
  let response;
3732
3743
  if (useStreaming) {
3733
- response = await provider.streamChat(this.conversationHistory, {
3744
+ response = await provider.streamChat(currentTurnMessages, {
3734
3745
  onText: (text) => {
3735
3746
  finalText += text;
3736
3747
  this.config.onText?.(text, false);
@@ -3746,7 +3757,7 @@ ${userMessage}`;
3746
3757
  this.config.onText?.("", true);
3747
3758
  }
3748
3759
  } else {
3749
- response = await provider.chat(this.conversationHistory);
3760
+ response = await provider.chat(currentTurnMessages);
3750
3761
  if (response.text) {
3751
3762
  finalText += response.text;
3752
3763
  this.config.onText?.(response.text, true);
@@ -3772,10 +3783,6 @@ ${userMessage}`;
3772
3783
  });
3773
3784
  }
3774
3785
  if (response.toolCalls.length === 0) {
3775
- this.conversationHistory.push({
3776
- role: "assistant",
3777
- content: responseContent
3778
- });
3779
3786
  break;
3780
3787
  }
3781
3788
  const toolResults = [];
@@ -3787,6 +3794,7 @@ ${userMessage}`;
3787
3794
  );
3788
3795
  const success2 = !(result && typeof result === "object" && "error" in result);
3789
3796
  this.config.onToolResult?.(toolCall2.name, result, success2);
3797
+ this.addToolHistory(toolCall2.name, toolCall2.input, success2);
3790
3798
  toolCalls.push({
3791
3799
  name: toolCall2.name,
3792
3800
  input: toolCall2.input,
@@ -3799,19 +3807,21 @@ ${userMessage}`;
3799
3807
  content: JSON.stringify(result)
3800
3808
  });
3801
3809
  }
3802
- this.conversationHistory.push({
3810
+ currentTurnMessages.push({
3803
3811
  role: "assistant",
3804
3812
  content: responseContent
3805
3813
  });
3806
- this.conversationHistory.push({
3814
+ currentTurnMessages.push({
3807
3815
  role: "user",
3808
3816
  content: toolResults
3809
3817
  });
3810
- this.truncateLastToolResults();
3811
3818
  if (response.stopReason === "end_turn" && response.toolCalls.length === 0) {
3812
3819
  break;
3813
3820
  }
3814
3821
  }
3822
+ if (finalText.trim()) {
3823
+ this.storeTextExchange(userMessage, finalText.trim());
3824
+ }
3815
3825
  return {
3816
3826
  text: finalText,
3817
3827
  toolCalls,
@@ -3900,17 +3910,11 @@ ${userMessage}`;
3900
3910
  const maxIterations = this.config.maxIterations ?? 15;
3901
3911
  const model = this.config.model ?? "claude-sonnet-4-5-20250929";
3902
3912
  const maxTokens = this.config.maxTokens ?? 8192;
3903
- const systemPrompt = this.config.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
3913
+ const systemPrompt = this.buildSystemContext();
3904
3914
  const useStreaming = this.config.streaming ?? true;
3905
3915
  const allTools = await this.getAllTools();
3906
3916
  const contextManagement = this.config.contextEditing && this.config.contextEditing.length > 0 ? { edits: this.config.contextEditing } : void 0;
3907
- const contextMessage = `[Working directory: ${this.workingDirectory}]
3908
-
3909
- ${userMessage}`;
3910
- this.conversationHistory.push({
3911
- role: "user",
3912
- content: contextMessage
3913
- });
3917
+ let currentTurnMessages = this.buildSlimHistory(userMessage);
3914
3918
  this.clearToolCallLoopTracking();
3915
3919
  const toolCalls = [];
3916
3920
  let iterations = 0;
@@ -3940,7 +3944,7 @@ ${userMessage}`;
3940
3944
  max_tokens: maxTokens,
3941
3945
  system: systemWithCache,
3942
3946
  tools: allTools,
3943
- messages: this.conversationHistory
3947
+ messages: currentTurnMessages
3944
3948
  };
3945
3949
  if (contextManagement) {
3946
3950
  streamOptions.context_management = contextManagement;
@@ -3982,7 +3986,7 @@ ${userMessage}`;
3982
3986
  max_tokens: maxTokens,
3983
3987
  system: systemWithCache,
3984
3988
  tools: allTools,
3985
- messages: this.conversationHistory
3989
+ messages: currentTurnMessages
3986
3990
  };
3987
3991
  if (contextManagement) {
3988
3992
  createOptions.context_management = contextManagement;
@@ -4003,10 +4007,6 @@ ${userMessage}`;
4003
4007
  }
4004
4008
  this.config.onStreamEnd?.();
4005
4009
  if (toolUses.length === 0) {
4006
- this.conversationHistory.push({
4007
- role: "assistant",
4008
- content: responseContent
4009
- });
4010
4010
  break;
4011
4011
  }
4012
4012
  const toolResults = [];
@@ -4018,6 +4018,7 @@ ${userMessage}`;
4018
4018
  );
4019
4019
  const success2 = !(result && typeof result === "object" && "error" in result);
4020
4020
  this.config.onToolResult?.(toolUse.name, result, success2);
4021
+ this.addToolHistory(toolUse.name, toolUse.input, success2);
4021
4022
  toolCalls.push({
4022
4023
  name: toolUse.name,
4023
4024
  input: toolUse.input,
@@ -4030,19 +4031,21 @@ ${userMessage}`;
4030
4031
  content: JSON.stringify(result)
4031
4032
  });
4032
4033
  }
4033
- this.conversationHistory.push({
4034
+ currentTurnMessages.push({
4034
4035
  role: "assistant",
4035
4036
  content: responseContent
4036
4037
  });
4037
- this.conversationHistory.push({
4038
+ currentTurnMessages.push({
4038
4039
  role: "user",
4039
4040
  content: toolResults
4040
4041
  });
4041
- this.truncateLastToolResults();
4042
4042
  if (response.stop_reason === "end_turn" && toolUses.length === 0) {
4043
4043
  break;
4044
4044
  }
4045
4045
  }
4046
+ if (finalText.trim()) {
4047
+ this.storeTextExchange(userMessage, finalText.trim());
4048
+ }
4046
4049
  return {
4047
4050
  text: finalText,
4048
4051
  toolCalls,
@@ -4055,6 +4058,9 @@ ${userMessage}`;
4055
4058
  */
4056
4059
  clearHistory() {
4057
4060
  this.conversationHistory = [];
4061
+ this.conversationSummary = null;
4062
+ this.toolHistory = [];
4063
+ this.exchanges = [];
4058
4064
  }
4059
4065
  /**
4060
4066
  * Get current conversation history
@@ -4062,6 +4068,126 @@ ${userMessage}`;
4062
4068
  getHistory() {
4063
4069
  return [...this.conversationHistory];
4064
4070
  }
4071
+ /**
4072
+ * Extract primary input from tool arguments for compact history.
4073
+ * Returns the most relevant parameter value, truncated if needed.
4074
+ */
4075
+ extractPrimaryInput(input) {
4076
+ const primaryKeys = ["query", "path", "command", "marketId", "content", "url", "pattern", "ticker"];
4077
+ for (const key of primaryKeys) {
4078
+ if (input[key] && typeof input[key] === "string") {
4079
+ const val = input[key];
4080
+ return val.length > 40 ? val.slice(0, 40) + "..." : val;
4081
+ }
4082
+ }
4083
+ for (const val of Object.values(input)) {
4084
+ if (typeof val === "string" && val.length > 0) {
4085
+ return val.length > 40 ? val.slice(0, 40) + "..." : val;
4086
+ }
4087
+ }
4088
+ const firstKey = Object.keys(input)[0];
4089
+ if (firstKey) {
4090
+ const val = String(input[firstKey]);
4091
+ return val.length > 40 ? val.slice(0, 40) + "..." : val;
4092
+ }
4093
+ return "(no input)";
4094
+ }
4095
+ /**
4096
+ * Add a tool call to history after execution.
4097
+ * Keeps only the last 10 entries.
4098
+ */
4099
+ addToolHistory(tool, input, success2) {
4100
+ this.toolHistory.push({
4101
+ tool,
4102
+ primaryInput: this.extractPrimaryInput(input),
4103
+ success: success2,
4104
+ timestamp: Date.now()
4105
+ });
4106
+ if (this.toolHistory.length > _Agent.MAX_TOOL_HISTORY) {
4107
+ this.toolHistory = this.toolHistory.slice(-_Agent.MAX_TOOL_HISTORY);
4108
+ }
4109
+ }
4110
+ /**
4111
+ * Format tool history for context injection.
4112
+ * Simple, clean format without emojis.
4113
+ */
4114
+ formatToolHistory() {
4115
+ if (this.toolHistory.length === 0) return "";
4116
+ const lines = this.toolHistory.map((t) => {
4117
+ const status = t.success ? "ok" : "failed";
4118
+ return `- ${t.tool}: "${t.primaryInput}" - ${status}`;
4119
+ });
4120
+ return "Recent actions:\n" + lines.join("\n");
4121
+ }
4122
+ /**
4123
+ * Add a user/model exchange to history.
4124
+ * If we exceed max exchanges, compact older ones first.
4125
+ * @deprecated Use storeTextExchange instead
4126
+ */
4127
+ /**
4128
+ * Build the full system context including tool history and summary.
4129
+ */
4130
+ buildSystemContext() {
4131
+ const basePrompt = this.config.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
4132
+ const parts = [basePrompt];
4133
+ const toolHistoryStr = this.formatToolHistory();
4134
+ if (toolHistoryStr) {
4135
+ parts.push(toolHistoryStr);
4136
+ }
4137
+ if (this.conversationSummary) {
4138
+ parts.push(`Previous context:
4139
+ ${this.conversationSummary}`);
4140
+ }
4141
+ return parts.join("\n\n");
4142
+ }
4143
+ /**
4144
+ * Build messages array from exchanges for API call.
4145
+ * Converts stored exchanges to MessageParam format.
4146
+ */
4147
+ buildMessagesFromExchanges() {
4148
+ const messages = [];
4149
+ for (const exchange of this.exchanges) {
4150
+ messages.push({ role: "user", content: exchange.userMessage });
4151
+ messages.push({ role: "assistant", content: exchange.assistantResponse });
4152
+ }
4153
+ return messages;
4154
+ }
4155
+ /**
4156
+ * Build slim history for API call: last 2 text exchanges + current user message.
4157
+ * NO tool calls, NO tool results - just text.
4158
+ */
4159
+ buildSlimHistory(currentUserMessage) {
4160
+ const messages = [];
4161
+ const recentExchanges = this.exchanges.slice(-2);
4162
+ for (const exchange of recentExchanges) {
4163
+ messages.push({ role: "user", content: exchange.userMessage });
4164
+ messages.push({ role: "assistant", content: exchange.assistantResponse });
4165
+ }
4166
+ messages.push({ role: "user", content: currentUserMessage });
4167
+ return messages;
4168
+ }
4169
+ /**
4170
+ * Store a text-only exchange (no tool calls).
4171
+ * Keeps only last 2 exchanges for context.
4172
+ */
4173
+ storeTextExchange(userMessage, assistantResponse) {
4174
+ this.exchanges.push({
4175
+ userMessage,
4176
+ assistantResponse,
4177
+ timestamp: Date.now()
4178
+ });
4179
+ if (this.exchanges.length > 2) {
4180
+ this.exchanges = this.exchanges.slice(-2);
4181
+ }
4182
+ }
4183
+ /**
4184
+ * Extract final text response from assistant content blocks.
4185
+ * Filters out tool_use blocks, returns only text.
4186
+ */
4187
+ extractTextResponse(content) {
4188
+ const textBlocks = content.filter((block) => block.type === "text");
4189
+ return textBlocks.map((block) => block.text).join("\n").trim();
4190
+ }
4065
4191
  /**
4066
4192
  * Set working directory
4067
4193
  */
@@ -4074,18 +4200,6 @@ ${userMessage}`;
4074
4200
  getWorkingDirectory() {
4075
4201
  return this.workingDirectory;
4076
4202
  }
4077
- /**
4078
- * Truncate tool results in the last message of conversation history.
4079
- *
4080
- * This is called AFTER Claude has seen the full tool results and responded.
4081
- * We then replace the full results with truncated versions to save context
4082
- * on future turns. This way:
4083
- * - Current turn: Claude sees full data, can display everything to user
4084
- * - Future turns: Only actionable data (IDs, prices) is in context
4085
- */
4086
- truncateLastToolResults() {
4087
- return;
4088
- }
4089
4203
  /**
4090
4204
  * Update cumulative token usage from API response
4091
4205
  * @param usage - Token counts from the API response
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantish/agent",
3
- "version": "0.1.51",
3
+ "version": "0.1.52",
4
4
  "description": "AI-powered agent for trading on Polymarket and Kalshi",
5
5
  "type": "module",
6
6
  "bin": {