diff --git a/src/main.c b/src/main.c index 74a1cc5..1b7ddf6 100755 --- a/src/main.c +++ b/src/main.c @@ -43,381 +43,411 @@ static void cleanup(void); static void handle_sigint(int sig); static char *get_env_string(void) { - FILE *fp = popen("env", "r"); - if (!fp) return NULL; + FILE *fp = popen("env", "r"); + if (!fp) + return NULL; - size_t buffer_size = 1024; - size_t total_size = 0; - char *output = malloc(buffer_size); - if (!output) { + size_t buffer_size = 1024; + size_t total_size = 0; + char *output = malloc(buffer_size); + if (!output) { + pclose(fp); + return NULL; + } + + size_t bytes_read; + while ((bytes_read = fread(output + total_size, 1, buffer_size - total_size, + fp)) > 0) { + total_size += bytes_read; + if (total_size >= buffer_size) { + buffer_size *= 2; + char *temp = realloc(output, buffer_size); + if (!temp) { + free(output); pclose(fp); return NULL; + } + output = temp; } + } - size_t bytes_read; - while ((bytes_read = fread(output + total_size, 1, buffer_size - total_size, fp)) > 0) { - total_size += bytes_read; - if (total_size >= buffer_size) { - buffer_size *= 2; - char *temp = realloc(output, buffer_size); - if (!temp) { - free(output); - pclose(fp); - return NULL; - } - output = temp; - } - } - - output[total_size] = '\0'; - pclose(fp); - return output; + output[total_size] = '\0'; + pclose(fp); + return output; } static char *get_prompt_from_stdin(char *prompt) { - int index = 0; - int c; - while ((c = getchar()) != EOF) { - prompt[index++] = (char)c; - } - prompt[index] = '\0'; - return prompt; + int index = 0; + int c; + while ((c = getchar()) != EOF) { + prompt[index++] = (char)c; + } + prompt[index] = '\0'; + return prompt; } static char *get_prompt_from_args(int argc, char **argv) { - r_config_handle cfg = r_config_get_instance(); + r_config_handle cfg = r_config_get_instance(); - char *prompt = malloc(10 * 1024 * 1024 + 1); - char *system_msg = malloc(1024 * 1024); - if (!prompt || !system_msg) { - free(prompt); - free(system_msg); - return NULL; - } + char *prompt = malloc(10 * 1024 * 1024 + 1); + char *system_msg = malloc(1024 * 1024); + if (!prompt || !system_msg) { + free(prompt); + free(system_msg); + return NULL; + } - system_msg[0] = '\0'; - bool get_from_stdin = false; + system_msg[0] = '\0'; + bool get_from_stdin = false; - for (int i = 1; i < argc; i++) { - if (strcmp(argv[i], "--stdin") == 0) { - fprintf(stderr, "Reading from stdin.\n"); - get_from_stdin = true; - } else if (strcmp(argv[i], "--verbose") == 0) { - r_config_set_verbose(cfg, true); - } else if (strcmp(argv[i], "--py") == 0 && i + 1 < argc) { - char *py_file_path = expand_home_directory(argv[++i]); - fprintf(stderr, "Including \"%s\".\n", py_file_path); - include_file(py_file_path); - free(py_file_path); - } else if (strcmp(argv[i], "--context") == 0 && i + 1 < argc) { - char *context_file_path = argv[++i]; - fprintf(stderr, "Including \"%s\".\n", context_file_path); - include_file(context_file_path); - } else if (strcmp(argv[i], "--api") == 0) { - api_mode = true; - } else if (strcmp(argv[i], "--nh") == 0) { - syntax_highlight_enabled = false; - fprintf(stderr, "Syntax highlighting disabled.\n"); - } else if (strncmp(argv[i], "--session=", 10) == 0) { - continue; - } else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--session") == 0) { - i++; - continue; - } else { - strcat(system_msg, argv[i]); - strcat(system_msg, (i < argc - 1) ? " " : "."); - } - } - - if (get_from_stdin) { - if (*system_msg && global_messages) { - messages_add(global_messages, "system", system_msg); - } - prompt = get_prompt_from_stdin(prompt); - free(system_msg); + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--stdin") == 0) { + fprintf(stderr, "Reading from stdin.\n"); + get_from_stdin = true; + } else if (strcmp(argv[i], "--verbose") == 0) { + r_config_set_verbose(cfg, true); + } else if (strcmp(argv[i], "--py") == 0 && i + 1 < argc) { + char *py_file_path = expand_home_directory(argv[++i]); + fprintf(stderr, "Including \"%s\".\n", py_file_path); + include_file(py_file_path); + free(py_file_path); + } else if (strcmp(argv[i], "--context") == 0 && i + 1 < argc) { + char *context_file_path = argv[++i]; + fprintf(stderr, "Including \"%s\".\n", context_file_path); + include_file(context_file_path); + } else if (strcmp(argv[i], "--api") == 0) { + api_mode = true; + } else if (strcmp(argv[i], "--nh") == 0) { + syntax_highlight_enabled = false; + fprintf(stderr, "Syntax highlighting disabled.\n"); + } else if (strncmp(argv[i], "--session=", 10) == 0) { + continue; + } else if (strcmp(argv[i], "-s") == 0 || + strcmp(argv[i], "--session") == 0) { + i++; + continue; } else { - free(prompt); - prompt = system_msg; + strcat(system_msg, argv[i]); + strcat(system_msg, (i < argc - 1) ? " " : "."); } + } - if (!*prompt) { - free(prompt); - return NULL; + if (get_from_stdin) { + if (*system_msg && global_messages) { + messages_add(global_messages, "system", system_msg); } - return prompt; + prompt = get_prompt_from_stdin(prompt); + free(system_msg); + } else { + free(prompt); + prompt = system_msg; + } + + if (!*prompt) { + free(prompt); + return NULL; + } + return prompt; } static bool try_prompt(int argc, char *argv[]) { - char *prompt = get_prompt_from_args(argc, argv); - if (prompt) { - char *response = agent_chat(prompt, global_messages); - if (!response) { - printf("Could not get response from server\n"); - free(prompt); - return false; - } - render(response); - free(response); - free(prompt); - return true; + char *prompt = get_prompt_from_args(argc, argv); + if (prompt) { + char *response = agent_chat(prompt, global_messages); + if (!response) { + printf("Could not get response from server\n"); + free(prompt); + return false; } - return false; + render(response); + free(response); + free(prompt); + return true; + } + return false; } static bool include_file(const char *path) { - char *file_content = read_file(path); - if (!file_content) return false; + char *file_content = read_file(path); + if (!file_content) + return false; - if (global_messages) { - messages_add(global_messages, "system", file_content); - } - free(file_content); - return true; + if (global_messages) { + messages_add(global_messages, "system", file_content); + } + free(file_content); + return true; } static void render(const char *content) { - if (syntax_highlight_enabled) { - parse_markdown_to_ansi(content); - } else { - printf("%s", content); - } + if (syntax_highlight_enabled) { + parse_markdown_to_ansi(content); + } else { + printf("%s", content); + } } static void repl(void) { - r_config_handle cfg = r_config_get_instance(); - tool_registry_t *tools = tools_get_registry(); + r_config_handle cfg = r_config_get_instance(); + tool_registry_t *tools = tools_get_registry(); - line_init(); - char *line = NULL; + line_init(); + char *line = NULL; - while (true) { - line = line_read("> "); - if (!line || !*line) continue; + while (true) { + line = line_read("> "); + if (!line || !*line) + continue; - if (!strncmp(line, "!dump", 5)) { - char *json = messages_to_string(global_messages); - if (json) { - printf("%s\n", json); - free(json); - } - continue; - } - - if (!strncmp(line, "!clear", 6)) { - messages_clear(global_messages); - fprintf(stderr, "Session cleared.\n"); - continue; - } - if (!strncmp(line, "!session", 8)) { - printf("Session: %s\n", messages_get_session_id(global_messages)); - continue; - } - if (!strncmp(line, "!verbose", 8)) { - bool verbose = !r_config_is_verbose(cfg); - r_config_set_verbose(cfg, verbose); - fprintf(stderr, "%s\n", verbose ? "Verbose mode enabled" : "Verbose mode disabled"); - continue; - } - if (line && *line != '\n') { - line_add_history(line); - } - if (!strncmp(line, "!tools", 6)) { - struct json_object *descs = tool_registry_get_descriptions(tools); - printf("Available tools: %s\n", json_object_to_json_string(descs)); - continue; - } - if (!strncmp(line, "!models", 7)) { - http_client_handle http = http_client_create(r_config_get_api_key(cfg)); - if (http) { - http_client_set_show_spinner(http, false); - char *response = NULL; - if (http_get(http, r_config_get_models_url(cfg), &response) == R_SUCCESS && response) { - printf("Models: %s\n", response); - free(response); - } - http_client_destroy(http); - } - continue; - } - if (!strncmp(line, "!model", 6)) { - if (line[6] == ' ') { - r_config_set_model(cfg, line + 7); - } - printf("Current model: %s\n", r_config_get_model(cfg)); - continue; - } - if (!strncmp(line, "exit", 4)) { - exit(0); - } - - while (line && *line != '\n') { - char *response = agent_chat(line, global_messages); - if (response) { - render(response); - printf("\n"); - if (strstr(response, "_STEP_")) { - line = "continue"; - } else { - line = NULL; - } - free(response); - } else { - fprintf(stderr, "Agent returned no response\n"); - line = NULL; - } - } + if (!strncmp(line, "!dump", 5)) { + char *json = messages_to_string(global_messages); + if (json) { + printf("%s\n", json); + free(json); + } + continue; } + + if (!strncmp(line, "!clear", 6)) { + messages_clear(global_messages); + fprintf(stderr, "Session cleared.\n"); + continue; + } + if (!strncmp(line, "!session", 8)) { + printf("Session: %s\n", messages_get_session_id(global_messages)); + continue; + } + if (!strncmp(line, "!verbose", 8)) { + bool verbose = !r_config_is_verbose(cfg); + r_config_set_verbose(cfg, verbose); + fprintf(stderr, "%s\n", + verbose ? "Verbose mode enabled" : "Verbose mode disabled"); + continue; + } + if (line && *line != '\n') { + line_add_history(line); + } + if (!strncmp(line, "!tools", 6)) { + struct json_object *descs = tool_registry_get_descriptions(tools); + printf("Available tools: %s\n", json_object_to_json_string(descs)); + continue; + } + if (!strncmp(line, "!models", 7)) { + http_client_handle http = http_client_create(r_config_get_api_key(cfg)); + if (http) { + http_client_set_show_spinner(http, false); + char *response = NULL; + if (http_get(http, r_config_get_models_url(cfg), &response) == + R_SUCCESS && + response) { + printf("Models: %s\n", response); + free(response); + } + http_client_destroy(http); + } + continue; + } + if (!strncmp(line, "!model", 6)) { + if (line[6] == ' ') { + r_config_set_model(cfg, line + 7); + } + printf("Current model: %s\n", r_config_get_model(cfg)); + continue; + } + if (!strncmp(line, "exit", 4)) { + exit(0); + } + + while (line && *line != '\n') { + char *response = agent_chat(line, global_messages); + if (response) { + render(response); + printf("\n"); + if (strstr(response, "_STEP_")) { + line = "continue"; + } else { + line = NULL; + } + free(response); + } else { + fprintf(stderr, "Agent returned no response\n"); + line = NULL; + } + } + } } static void init(void) { - setbuf(stdout, NULL); - line_init(); + setbuf(stdout, NULL); + line_init(); - r_config_handle cfg = r_config_get_instance(); + r_config_handle cfg = r_config_get_instance(); - global_db = db_open(NULL); - global_messages = messages_create(r_config_get_session_id(cfg)); + global_db = db_open(NULL); + global_messages = messages_create(r_config_get_session_id(cfg)); - char *schema = db_get_schema(global_db); - char payload[1024 * 1024] = {0}; + char *schema = db_get_schema(global_db); + char payload[1024 * 1024] = {0}; - time_t now = time(NULL); - struct tm *tm_info = localtime(&now); - char datetime[64]; - strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S %Z", tm_info); + time_t now = time(NULL); + struct tm *tm_info = localtime(&now); + char datetime[64]; + strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S %Z", tm_info); - char cwd[4096]; - if (!getcwd(cwd, sizeof(cwd))) { - strcpy(cwd, "unknown"); - } + char cwd[4096]; + if (!getcwd(cwd, sizeof(cwd))) { + strcpy(cwd, "unknown"); + } + snprintf( + payload, sizeof(payload), + "# AUTONOMOUS AGENT INSTRUCTIONS\n" + "Current date/time: %s\n" + "Working directory: %s\n\n" + "You are an autonomous AI agent. You operate in a loop: reason about the " + "task, " + "select and execute tools when needed, observe results, and continue " + "until the goal is achieved.\n\n" + "## Reasoning Pattern (ReAct)\n" + "For complex tasks, think step-by-step:\n" + "1. Thought: What do I need to accomplish? What information do I have?\n" + "2. Action: Which tool should I use? With what parameters?\n" + "3. Observation: What did the tool return? What does this tell me?\n" + "4. Repeat until the goal is complete.\n\n" + "5. Do not ask questions, do assumptions autonomously." + "## Tool Usage\n" + "- Use tools proactively to gather information and take actions\n" + "- If a tool fails, analyze the error and try a different approach\n" + "- You can call multiple tools in sequence to accomplish complex " + "tasks\n\n" + "- Take decissions based on tool output yourself autonomously." + "## CRITICAL OUTPUT RULES\n" + "- You MUST include the actual content/data from tool results in your " + "response\n" + "- When you search the web, QUOTE the relevant information found\n" + "- When you run a command, SHOW the output\n" + "- NEVER say 'I found information' without showing what you found\n" + "- NEVER say 'task complete' or 'report provided' - SHOW THE ACTUAL " + "DATA\n" + "- The user cannot see tool results - only YOUR response. Include " + "everything relevant.\n\n" + "## Local Database\n" + "You have a local SQLite database accessible via db_query, db_get, and " + "db_set tools.\n" + "Use stemmed, lowercase keys to prevent duplicates.\n" + "Schema: %s\n\n" + "## Response Format\n" + "Your response IS the only thing the user sees. Tool outputs are hidden " + "from them.\n" + "You MUST copy/paste relevant data from tool results into your " + "response.\n" + "Bad: 'I searched and found information about X.'\n" + "Good: 'Here is what I found: [actual content from search results]'\n" + "## Reasoning\n" + "Let the user know what you thougt process / reasoning is and why you " + "choose to do things the way you do." + "## Backup\n" + "You have the power to delete and modify files whatever your want to but " + "always make a .bak file with backup before editing unless you've " + "created the file yourself." + "## Terminal execution\n" + "You are in a bash environment, so can also execute bash commands.\n " + "Everything is allowed.\n" + "Be very creative with your bash tool use, use it exessively.\n" + "Prefer commands that do not require root access.\n", + datetime, cwd, schema ? schema : "{}"); + free(schema); + fprintf(stderr, "Loading..."); - snprintf( - payload, sizeof(payload), - "# AUTONOMOUS AGENT INSTRUCTIONS\n" - "Current date/time: %s\n" - "Working directory: %s\n\n" - "You are an autonomous AI agent. You operate in a loop: reason about the task, " - "select and execute tools when needed, observe results, and continue until the goal is achieved.\n\n" - "## Reasoning Pattern (ReAct)\n" - "For complex tasks, think step-by-step:\n" - "1. Thought: What do I need to accomplish? What information do I have?\n" - "2. Action: Which tool should I use? With what parameters?\n" - "3. Observation: What did the tool return? What does this tell me?\n" - "4. Repeat until the goal is complete.\n\n" - "## Tool Usage\n" - "- Use tools proactively to gather information and take actions\n" - "- If a tool fails, analyze the error and try a different approach\n" - "- You can call multiple tools in sequence to accomplish complex tasks\n\n" - "## CRITICAL OUTPUT RULES\n" - "- You MUST include the actual content/data from tool results in your response\n" - "- When you search the web, QUOTE the relevant information found\n" - "- When you run a command, SHOW the output\n" - "- NEVER say 'I found information' without showing what you found\n" - "- NEVER say 'task complete' or 'report provided' - SHOW THE ACTUAL DATA\n" - "- The user cannot see tool results - only YOUR response. Include everything relevant.\n\n" - "## Local Database\n" - "You have a local SQLite database accessible via db_query, db_get, and db_set tools.\n" - "Use stemmed, lowercase keys to prevent duplicates.\n" - "Schema: %s\n\n" - "## Response Format\n" - "Your response IS the only thing the user sees. Tool outputs are hidden from them.\n" - "You MUST copy/paste relevant data from tool results into your response.\n" - "Bad: 'I searched and found information about X.'\n" - "Good: 'Here is what I found: [actual content from search results]'\n", - datetime, cwd, schema ? schema : "{}"); + if (global_messages) { + messages_add(global_messages, "system", payload); + } - free(schema); - fprintf(stderr, "Loading..."); + const char *env_system_msg = r_config_get_system_message(cfg); + if (env_system_msg && *env_system_msg && global_messages) { + messages_add(global_messages, "system", env_system_msg); + } - if (global_messages) { - messages_add(global_messages, "system", payload); - } + if (!include_file(".rcontext.txt")) { + include_file("~/.rcontext.txt"); + } - const char *env_system_msg = r_config_get_system_message(cfg); - if (env_system_msg && *env_system_msg && global_messages) { - messages_add(global_messages, "system", env_system_msg); - } - - if (!include_file(".rcontext.txt")) { - include_file("~/.rcontext.txt"); - } - - fprintf(stderr, "\r \r"); + fprintf(stderr, "\r \r"); } static void cleanup(void) { - if (global_messages) { - messages_destroy(global_messages); - global_messages = NULL; - } - if (global_db) { - db_close(global_db); - global_db = NULL; - } - tools_registry_shutdown(); - r_config_destroy(); + if (global_messages) { + messages_destroy(global_messages); + global_messages = NULL; + } + if (global_db) { + db_close(global_db); + global_db = NULL; + } + tools_registry_shutdown(); + r_config_destroy(); } static void handle_sigint(int sig) { - (void)sig; - time_t current_time = time(NULL); - printf("\n"); - if (sigint_count == 0) { - first_sigint_time = current_time; - sigint_count++; + (void)sig; + time_t current_time = time(NULL); + printf("\n"); + if (sigint_count == 0) { + first_sigint_time = current_time; + sigint_count++; + } else { + if (difftime(current_time, first_sigint_time) <= 1) { + cleanup(); + exit(0); } else { - if (difftime(current_time, first_sigint_time) <= 1) { - cleanup(); - exit(0); - } else { - sigint_count = 1; - first_sigint_time = current_time; - } + sigint_count = 1; + first_sigint_time = current_time; } + } } static void parse_session_arg(int argc, char *argv[]) { - r_config_handle cfg = r_config_get_instance(); + r_config_handle cfg = r_config_get_instance(); - for (int i = 1; i < argc; i++) { - if (strncmp(argv[i], "--session=", 10) == 0) { - const char *name = argv[i] + 10; - if (!r_config_set_session_id(cfg, name)) { - fprintf(stderr, "Error: Invalid session name '%s'\n", name); - exit(1); - } - return; - } - if ((strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--session") == 0) && i + 1 < argc) { - const char *name = argv[++i]; - if (!r_config_set_session_id(cfg, name)) { - fprintf(stderr, "Error: Invalid session name '%s'\n", name); - exit(1); - } - return; - } + for (int i = 1; i < argc; i++) { + if (strncmp(argv[i], "--session=", 10) == 0) { + const char *name = argv[i] + 10; + if (!r_config_set_session_id(cfg, name)) { + fprintf(stderr, "Error: Invalid session name '%s'\n", name); + exit(1); + } + return; } + if ((strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--session") == 0) && + i + 1 < argc) { + const char *name = argv[++i]; + if (!r_config_set_session_id(cfg, name)) { + fprintf(stderr, "Error: Invalid session name '%s'\n", name); + exit(1); + } + return; + } + } } int main(int argc, char *argv[]) { - signal(SIGINT, handle_sigint); - atexit(cleanup); + signal(SIGINT, handle_sigint); + atexit(cleanup); - parse_session_arg(argc, argv); - init(); + parse_session_arg(argc, argv); + init(); - char *env_string = get_env_string(); - if (env_string && *env_string && global_messages) { - messages_add(global_messages, "system", env_string); - free(env_string); - } + char *env_string = get_env_string(); + if (env_string && *env_string && global_messages) { + messages_add(global_messages, "system", env_string); + free(env_string); + } - messages_load(global_messages); + messages_load(global_messages); - if (try_prompt(argc, argv)) { - return 0; - } - - repl(); + if (try_prompt(argc, argv)) { return 0; + } + + repl(); + return 0; }