@votadev/tooncode 2.2.8 → 2.3.0

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 (2) hide show
  1. package/package.json +1 -1
  2. package/tooncode.py +45 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@votadev/tooncode",
3
- "version": "2.2.8",
3
+ "version": "2.3.0",
4
4
  "description": "🇹🇭 Thai Coding Agent CLI — Claude Code alternative powered by free models. 20 tools, multi-agent team, browser automation, MCP servers.",
5
5
  "author": "VotaLab",
6
6
  "license": "MIT",
package/tooncode.py CHANGED
@@ -8,7 +8,7 @@ Usage:
8
8
  python tooncode.py
9
9
  """
10
10
 
11
- VERSION = "2.2.8"
11
+ VERSION = "2.3.0"
12
12
 
13
13
  import httpx
14
14
  import json
@@ -2917,27 +2917,53 @@ def _get_current_api_url() -> str:
2917
2917
 
2918
2918
  def _validate_messages(messages: list):
2919
2919
  """Fix message ordering issues before sending to API."""
2920
- # Rule: tool_result must follow assistant with tool_use
2921
- # Rule: alternating user/assistant (with tool_result counting as user)
2920
+ # Rules:
2921
+ # 1. Must alternate user/assistant
2922
+ # 2. tool_result (in user msg) must come right after assistant with tool_use
2923
+ # 3. First message must be user
2922
2924
  fixed = []
2923
- for i, msg in enumerate(messages):
2925
+ for msg in messages:
2924
2926
  role = msg.get("role", "")
2925
- # Skip empty messages
2926
2927
  content = msg.get("content", [])
2927
2928
  if not content:
2928
2929
  continue
2929
- # Prevent consecutive same-role messages (except tool_result after user)
2930
- if fixed:
2931
- prev_role = fixed[-1].get("role", "")
2932
- if role == prev_role and role == "user":
2933
- # Check if this is tool_result (allowed after tool_use response)
2934
- is_tool_result = isinstance(content, list) and any(
2935
- b.get("type") == "tool_result" for b in content if isinstance(b, dict))
2936
- if not is_tool_result:
2937
- fixed.append({"role": "assistant", "content": [{"type": "text", "text": "(continuing)"}]})
2938
- elif role == prev_role and role == "assistant":
2930
+
2931
+ # Detect message types
2932
+ is_tool_result = isinstance(content, list) and any(
2933
+ isinstance(b, dict) and b.get("type") == "tool_result" for b in content)
2934
+ has_tool_use = isinstance(content, list) and any(
2935
+ isinstance(b, dict) and b.get("type") == "tool_use" for b in content)
2936
+
2937
+ if not fixed:
2938
+ # First message must be user
2939
+ if role != "user":
2940
+ fixed.append({"role": "user", "content": [{"type": "text", "text": "(start)"}]})
2941
+ fixed.append(msg)
2942
+ continue
2943
+
2944
+ prev = fixed[-1]
2945
+ prev_role = prev.get("role", "")
2946
+ prev_content = prev.get("content", [])
2947
+ prev_has_tool_use = isinstance(prev_content, list) and any(
2948
+ isinstance(b, dict) and b.get("type") == "tool_use" for b in prev_content)
2949
+
2950
+ # tool_result must follow assistant with tool_use
2951
+ if is_tool_result and role == "user":
2952
+ if prev_role != "assistant" or not prev_has_tool_use:
2953
+ # Remove this tool_result — it's orphaned
2954
+ continue
2955
+ fixed.append(msg)
2956
+ continue
2957
+
2958
+ # Fix consecutive same role
2959
+ if role == prev_role:
2960
+ if role == "user":
2961
+ fixed.append({"role": "assistant", "content": [{"type": "text", "text": "(continuing)"}]})
2962
+ else:
2939
2963
  fixed.append({"role": "user", "content": [{"type": "text", "text": "(continue)"}]})
2964
+
2940
2965
  fixed.append(msg)
2966
+
2941
2967
  messages.clear()
2942
2968
  messages.extend(fixed)
2943
2969
 
@@ -4981,6 +5007,10 @@ def main(_initial_prompt=None):
4981
5007
  def _(event):
4982
5008
  raise EOFError()
4983
5009
 
5010
+ @kb.add("escape", "escape")
5011
+ def _(event):
5012
+ event.current_buffer.reset()
5013
+
4984
5014
  def _bottom_toolbar():
4985
5015
  """Claude Code-style bottom toolbar."""
4986
5016
  ctx_max = CONTEXT_WINDOWS.get(MODEL, 200_000)