import time import json import logging from pr.ui import Colors, display_tool_call, print_autonomous_header from pr.autonomous.detection import is_task_complete from pr.core.context import truncate_tool_result logger = logging.getLogger('pr') def run_autonomous_mode(assistant, task): assistant.autonomous_mode = True assistant.autonomous_iterations = 0 logger.debug(f"=== AUTONOMOUS MODE START ===") logger.debug(f"Task: {task}") if assistant.verbose: print_autonomous_header(task) assistant.messages.append({ "role": "user", "content": f"AUTONOMOUS TASK: {task}\n\nPlease work on this task step by step. Use tools as needed. When the task is fully complete, clearly state 'Task complete'." }) try: while True: assistant.autonomous_iterations += 1 logger.debug(f"--- Autonomous iteration {assistant.autonomous_iterations} ---") logger.debug(f"Messages before context management: {len(assistant.messages)}") if assistant.verbose: print(f"\n{Colors.BOLD}{Colors.MAGENTA}{'▶' * 3} Iteration {assistant.autonomous_iterations} {'◀' * 3}{Colors.RESET}\n") from pr.core.context import manage_context_window assistant.messages = manage_context_window(assistant.messages, assistant.verbose) logger.debug(f"Messages after context management: {len(assistant.messages)}") if assistant.verbose: print(f"{Colors.GRAY}Calling API...{Colors.RESET}") from pr.core.api import call_api from pr.tools.base import get_tools_definition response = call_api( assistant.messages, assistant.model, assistant.api_url, assistant.api_key, assistant.use_tools, get_tools_definition(), verbose=assistant.verbose ) if 'error' in response: logger.error(f"API error in autonomous mode: {response['error']}") print(f"{Colors.RED}Error: {response['error']}{Colors.RESET}") break is_complete = is_task_complete(response, assistant.autonomous_iterations) logger.debug(f"Task completion check: {is_complete}") if is_complete: result = process_response_autonomous(assistant, response) print(f"\n{Colors.GREEN}r:{Colors.RESET} {result}\n") logger.debug(f"=== AUTONOMOUS MODE COMPLETE ===") logger.debug(f"Total iterations: {assistant.autonomous_iterations}") logger.debug(f"Final message count: {len(assistant.messages)}") print(f"{Colors.BOLD}Total Iterations:{Colors.RESET} {assistant.autonomous_iterations}") print(f"{Colors.BOLD}Messages in Context:{Colors.RESET} {len(assistant.messages)}\n") break result = process_response_autonomous(assistant, response) if result: print(f"\n{Colors.GREEN}r:{Colors.RESET} {result}\n") time.sleep(0.5) except KeyboardInterrupt: logger.debug("Autonomous mode interrupted by user") print(f"\n{Colors.YELLOW}Autonomous mode interrupted by user{Colors.RESET}") finally: assistant.autonomous_mode = False logger.debug("=== AUTONOMOUS MODE END ===") def process_response_autonomous(assistant, response): if 'error' in response: return f"Error: {response['error']}" if 'choices' not in response or not response['choices']: return "No response from API" message = response['choices'][0]['message'] assistant.messages.append(message) if 'tool_calls' in message and message['tool_calls']: print(f"{Colors.BOLD}{Colors.CYAN}🔧 Executing {len(message['tool_calls'])} tool(s)...{Colors.RESET}\n") tool_results = [] for tool_call in message['tool_calls']: func_name = tool_call['function']['name'] arguments = json.loads(tool_call['function']['arguments']) display_tool_call(func_name, arguments, "running") result = execute_single_tool(assistant, func_name, arguments) result = truncate_tool_result(result) status = "success" if result.get("status") == "success" else "error" display_tool_call(func_name, arguments, status, result) tool_results.append({ "tool_call_id": tool_call['id'], "role": "tool", "content": json.dumps(result) }) for result in tool_results: assistant.messages.append(result) print(f"{Colors.GRAY}Processing tool results...{Colors.RESET}\n") from pr.core.api import call_api from pr.tools.base import get_tools_definition follow_up = call_api( assistant.messages, assistant.model, assistant.api_url, assistant.api_key, assistant.use_tools, get_tools_definition(), verbose=assistant.verbose ) return process_response_autonomous(assistant, follow_up) content = message.get('content', '') from pr.ui import render_markdown return render_markdown(content, assistant.syntax_highlighting) def execute_single_tool(assistant, func_name, arguments): logger.debug(f"Executing tool in autonomous mode: {func_name}") logger.debug(f"Tool arguments: {arguments}") from pr.tools import ( http_fetch, run_command, run_command_interactive, read_file, write_file, list_directory, mkdir, chdir, getpwd, db_set, db_get, db_query, web_search, web_search_news, python_exec, index_source_directory, search_replace, open_editor, editor_insert_text, editor_replace_text, editor_search, close_editor, create_diff, apply_patch, tail_process, kill_process ) from pr.tools.patch import display_file_diff from pr.tools.filesystem import display_edit_summary, display_edit_timeline, clear_edit_tracker func_map = { 'http_fetch': lambda **kw: http_fetch(**kw), 'run_command': lambda **kw: run_command(**kw), 'tail_process': lambda **kw: tail_process(**kw), 'kill_process': lambda **kw: kill_process(**kw), 'run_command_interactive': lambda **kw: run_command_interactive(**kw), 'read_file': lambda **kw: read_file(**kw), 'write_file': lambda **kw: write_file(**kw, db_conn=assistant.db_conn), 'list_directory': lambda **kw: list_directory(**kw), 'mkdir': lambda **kw: mkdir(**kw), 'chdir': lambda **kw: chdir(**kw), 'getpwd': lambda **kw: getpwd(**kw), 'db_set': lambda **kw: db_set(**kw, db_conn=assistant.db_conn), 'db_get': lambda **kw: db_get(**kw, db_conn=assistant.db_conn), 'db_query': lambda **kw: db_query(**kw, db_conn=assistant.db_conn), 'web_search': lambda **kw: web_search(**kw), 'web_search_news': lambda **kw: web_search_news(**kw), 'python_exec': lambda **kw: python_exec(**kw, python_globals=assistant.python_globals), 'index_source_directory': lambda **kw: index_source_directory(**kw), 'search_replace': lambda **kw: search_replace(**kw), 'open_editor': lambda **kw: open_editor(**kw), 'editor_insert_text': lambda **kw: editor_insert_text(**kw), 'editor_replace_text': lambda **kw: editor_replace_text(**kw), 'editor_search': lambda **kw: editor_search(**kw), 'close_editor': lambda **kw: close_editor(**kw), 'create_diff': lambda **kw: create_diff(**kw), 'apply_patch': lambda **kw: apply_patch(**kw), 'display_file_diff': lambda **kw: display_file_diff(**kw), 'display_edit_summary': lambda **kw: display_edit_summary(), 'display_edit_timeline': lambda **kw: display_edit_timeline(**kw), 'clear_edit_tracker': lambda **kw: clear_edit_tracker(), } if func_name in func_map: try: result = func_map[func_name](**arguments) logger.debug(f"Tool execution result: {str(result)[:200]}...") return result except Exception as e: logger.error(f"Tool execution error: {str(e)}") return {"status": "error", "error": str(e)} else: logger.error(f"Unknown function requested: {func_name}") return {"status": "error", "error": f"Unknown function: {func_name}"}