@votadev/tooncode 2.2.3 → 2.2.5

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 +44 -14
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@votadev/tooncode",
3
- "version": "2.2.3",
3
+ "version": "2.2.5",
4
4
  "description": "Free AI 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.3"
11
+ VERSION = "2.2.5"
12
12
 
13
13
  import httpx
14
14
  import json
@@ -2915,10 +2915,40 @@ def _get_current_api_url() -> str:
2915
2915
  return _get_api_url()
2916
2916
 
2917
2917
 
2918
+ def _validate_messages(messages: list):
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)
2922
+ fixed = []
2923
+ for i, msg in enumerate(messages):
2924
+ role = msg.get("role", "")
2925
+ # Skip empty messages
2926
+ content = msg.get("content", [])
2927
+ if not content:
2928
+ 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":
2939
+ fixed.append({"role": "user", "content": [{"type": "text", "text": "(continue)"}]})
2940
+ fixed.append(msg)
2941
+ messages.clear()
2942
+ messages.extend(fixed)
2943
+
2944
+
2918
2945
  def stream_response(messages: list, renderer: StreamRenderer) -> dict:
2919
2946
  """Send request and stream the response, returning parsed content blocks."""
2920
2947
  global total_input_tokens, total_output_tokens, last_input_tokens
2921
2948
 
2949
+ # Validate message ordering
2950
+ _validate_messages(messages)
2951
+
2922
2952
  system_prompt = build_system_prompt()
2923
2953
  body = {
2924
2954
  "model": MODEL,
@@ -3285,7 +3315,7 @@ Be specific. Give copy-paste ready solutions."""
3285
3315
  console.print("[bold magenta][bosshelp] Asking Claude Code...[/bold magenta]")
3286
3316
  try:
3287
3317
  result = subprocess.run(
3288
- [claude_cmd, "--print"],
3318
+ [claude_cmd, "--print", "--dangerously-skip-permissions"],
3289
3319
  input=help_prompt,
3290
3320
  capture_output=True, text=True, cwd=CWD, timeout=180,
3291
3321
  encoding="utf-8", errors="replace",
@@ -4197,7 +4227,7 @@ OUTPUT THE JSON ARRAY NOW:"""
4197
4227
  console.print("[dim]Using Claude Code CLI...[/dim]")
4198
4228
  try:
4199
4229
  result = subprocess.run(
4200
- [claude_cmd, "--print"],
4230
+ [claude_cmd, "--print", "--dangerously-skip-permissions"],
4201
4231
  input=boss_prompt,
4202
4232
  capture_output=True, text=True, cwd=CWD, timeout=180,
4203
4233
  encoding="utf-8", errors="replace",
@@ -5312,21 +5342,21 @@ def main(_initial_prompt=None):
5312
5342
  )
5313
5343
  _last_errors.clear()
5314
5344
  _post_bosshelp = True
5315
- # Inject boss answer as a separate user message after tool results
5345
+ # MUST append tool_results first (API requires it after tool_use)
5316
5346
  messages.append({"role": "user", "content": tool_results})
5317
- messages.append({"role": "assistant", "content": [{"type": "text", "text": "I'm stuck. Let me check the boss's advice."}]})
5318
- # Parse boss answer for file paths and code blocks
5319
- fix_instructions = f"[BOSSHELP - APPLY NOW]\n{boss_answer}\n\n"
5320
- fix_instructions += "STEP-BY-STEP INSTRUCTIONS:\n"
5321
- fix_instructions += "1. Read each file mentioned above using the read tool\n"
5322
- fix_instructions += "2. Apply each code change using the edit tool (oldString → newString)\n"
5323
- fix_instructions += "3. If the fix says to run a command, use the bash tool\n"
5324
- fix_instructions += "4. After applying, verify with bash (e.g. python -c 'import module')\n"
5325
- fix_instructions += "\nDo NOT explain. Do NOT repeat failed approach. USE TOOLS NOW."
5347
+ # Then add bosshelp as assistant+user pair
5348
+ fix_instructions = f"[BOSSHELP]\n{boss_answer}\n\n"
5349
+ fix_instructions += "APPLY THIS FIX NOW:\n"
5350
+ fix_instructions += "1. Read each file mentioned\n"
5351
+ fix_instructions += "2. Use edit tool to apply changes\n"
5352
+ fix_instructions += "3. Run commands if needed\n"
5353
+ fix_instructions += "4. Verify the fix works\n"
5354
+ fix_instructions += "USE TOOLS. Do NOT explain."
5355
+ messages.append({"role": "assistant", "content": [{"type": "text", "text": "Let me apply the fix."}]})
5326
5356
  messages.append({"role": "user", "content": [{"type": "text", "text":
5327
5357
  fix_instructions,
5328
5358
  "cache_control": {"type": "ephemeral"}}]})
5329
- continue # Skip the normal append below
5359
+ continue
5330
5360
  else:
5331
5361
  # Successful tool = reset error tracking
5332
5362
  _last_errors.clear()