#include "agent.h" #include "db_utils.h" #include "line.h" #include "markdown.h" #include "openai.h" #include "r.h" #include "tools.h" #include "utils.h" #include #include #include #include #include #include #include #include volatile sig_atomic_t sigint_count = 0; time_t first_sigint_time = 0; bool SYNTAX_HIGHLIGHT_ENABLED = true; bool API_MODE = false; static void render(const char *content); static bool openai_include(const char *path); static char *get_prompt_from_stdin(char *prompt); static char *get_prompt_from_args(int argc, char **argv); static bool try_prompt(int argc, char *argv[]); static void repl(void); static void init(void); static void handle_sigint(int sig); char *get_env_string() { FILE *fp = popen("env", "r"); if (fp == NULL) { perror("popen failed"); return NULL; } size_t buffer_size = 1024; size_t total_size = 0; char *output = malloc(buffer_size); if (output == NULL) { perror("malloc failed"); 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 == NULL) { perror("realloc failed"); free(output); pclose(fp); return NULL; } output = temp; } } // Null-terminate the 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; } static char *get_prompt_from_args(int argc, char **argv) { char *prompt = malloc(10 * 1024 * 1024 + 1); char *system = malloc(1024 * 1024); if (!prompt || !system) { fprintf(stderr, "Error: Memory allocation failed.\n"); free(prompt); free(system); return NULL; } bool get_from_std_in = false; for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "--stdin") == 0) { fprintf(stderr, "Reading from stdin.\n"); get_from_std_in = true; } else if (strcmp(argv[i], "--verbose") == 0) { is_verbose = 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); openai_include(py_file_path); free(py_file_path); } else if (strcmp(argv[i], "--free") == 0) { auth_free(); } else if (strcmp(argv[i], "--context") == 0 && i + 1 < argc) { char *context_file_path = argv[++i]; fprintf(stderr, "Including \"%s\".\n", context_file_path); openai_include(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, argv[i]); strcat(system, (i < argc - 1) ? " " : "."); } } if (get_from_std_in) { if (*system) openai_system(system); prompt = get_prompt_from_stdin(prompt); } else { free(prompt); prompt = system; } 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); if (!response) { printf("Could not get response from server\n"); free(prompt); return false; } render(response); free(response); free(prompt); return true; } return false; } static bool openai_include(const char *path) { char *file_content = read_file(path); if (!file_content) return false; openai_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); } } static void repl(void) { line_init(); char *line = NULL; while (true) { line = line_read("> "); if (!line || !*line) continue; if (!strncmp(line, "!dump", 5)) { printf("%s\n", message_json()); continue; } if (!strncmp(line, "!clear", 6)) { messages_remove(); fprintf(stderr, "Session cleared.\n"); continue; } if (!strncmp(line, "!session", 8)) { init_session_id(); printf("Session: %s\n", get_session_id()); printf("DB Key: %s\n", get_session_db_key()); continue; } if (!strncmp(line, "!verbose", 8)) { is_verbose = !is_verbose; fprintf(stderr, "%s\n", is_verbose ? "Verbose mode enabled" : "Verbose mode disabled"); continue; } if (line && *line != '\n') line_add_history(line); if (!strncmp(line, "!tools", 6)) { printf("Available tools: %s\n", json_object_to_json_string(tools_descriptions())); continue; } if (!strncmp(line, "!models", 7)) { printf("Current model: %s\n", openai_fetch_models()); continue; } if (!strncmp(line, "!model", 6)) { if (line[6] == ' ') { set_prompt_model(line + 7); } printf("Current model: %s\n", get_prompt_model()); continue; } if (!strncmp(line, "exit", 4)) exit(0); while (line && *line != '\n') { char *response = agent_chat(line); 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(); auth_init(); db_initialize(); char *schema = db_get_schema(); 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); 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" "## 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); free(schema); fprintf(stderr, "Loading..."); openai_system(payload); char *env_system_message = get_env_system_message(); if (env_system_message && *env_system_message) { openai_system(env_system_message); free(env_system_message); } if (!openai_include(".rcontext.txt")) { openai_include("~/.rcontext.txt"); } fprintf(stderr, "\r \r"); } static void handle_sigint(int 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) { exit(0); } else { sigint_count = 1; first_sigint_time = current_time; } } } static void parse_session_arg(int argc, char *argv[]) { for (int i = 1; i < argc; i++) { if (strncmp(argv[i], "--session=", 10) == 0) { const char *name = argv[i] + 10; if (!set_session_id(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 (!set_session_id(name)) { fprintf(stderr, "Error: Invalid session name '%s'\n", name); exit(1); } return; } } char *env_session = get_env_session(); if (env_session) { if (!set_session_id(env_session)) { fprintf(stderr, "Warning: Invalid R_SESSION '%s', using default\n", env_session); } } } int main(int argc, char *argv[]) { signal(SIGINT, handle_sigint); parse_session_arg(argc, argv); init(); char *env_string = get_env_string(); if (env_string && *env_string) { openai_system(env_string); free(env_string); } messages_load_conversation(); if (try_prompt(argc, argv)) return 0; repl(); return 0; }